Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a hidden static method compile under Sun JDK 6 but cause a compile failure under OpenJDK 6 and 7?

The following class:

public class StaticMethodsDemo {

    public static class A {
        public static A make() { return new A(); };
    }
    public static class B extends A {
        public static B make() { return new B(); };
    }
    public static class BPrime<T> extends A {
        public static <T> BPrime<T> make() { return new BPrime<T>(); };
    }

    public static void main(String[] args) {
        B.make();
        // compiles under Sun JDK 1.6.0_20 but fails under Oracle JDK 1.7.0_01. Why?
        BPrime.<Object>make();
    }
}

compiles under Sun JDK 1.6.0_20 (Windows 64-bit, but shouldn't make a difference) but fails under Oracle JDK 1.7.0_01 (same platform) and OpenJDK 1.6.0_20 (Ubuntu) [1] with:

[ERROR] StaticMethodsDemo.java:[37,14] error: reference to make is ambiguous, both method make() in A and method <T>make() in BPrime match

Why? How does the generic parameter (which should be erased, no?) cause this apparent mismatch. Note that removing generics as follows:

...
public static class BPrime<T> extends A {
    T val;
    public static BPrime<?> make() { return new BPrime<Object>(); };
    public void setT(T val) { this.val = val; }
}

public static void main(String[] args) {
    B.make();
    BPrime<Long> bprime = (BPrime<Long>) BPrime.make();
    bprime.setT(Long.valueOf(10));
}

compiles and runs too (so the generics hack doesn't cause any weird runtime casting errors).

Issue 461: jclouds-core compilation fails using stock ubuntu openjdk

like image 731
Andrew Phillips Avatar asked Dec 23 '11 21:12

Andrew Phillips


People also ask

What is compile error in hidden module error?

This Compile Error in Hidden module error message usually appears when there are dome 32-bit add-ins versions of office which are incompatible with the newer version. The most common reason behind this error is when some conditions come true:

What to do after installing the JDK and JRE?

After Installing the JDK and JRE adds the java command to your command line. You can verify this through the command prompt by the java -version command. In some cases, you need to restart your system after installing the JDK. You can use the JDK compiler to convert your Java text file into an executable program.

What is the Java Runtime Environment in JDK?

An archiver (jar) and many more. The Java Runtime Environment in JDK is usually called Private Runtime because it is separated from the regular JRE and has extra contents.

How to set up JDK in your development environment?

Setting up JDK in your development environment is super easy, just follow the below simple steps. Select the latest JDK version and click Download and add it to your classpath. Just check the JDK software is installed or not on your computer at the correct location, for example, at C:\Program Files\Java\jdk11.0.9.


1 Answers

Obviously, the javac6's behavior is reasonable, and javac7's not.

Unfortunately, according to the letter of the spec, javac7 is right.

This is due to the root of all evil in java - type erasure. The motivation is to generify collection APIs without breaking any old code that reference the old, non-generified collection API. For the purpose of brevity let's refer to it as the dumbest motivation.

When compiling BPrime.<Object>make(), first javac needs to figure out the class containing the method. That is easily class B'. ( http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.1 )

Then we need to know all methods in class B', including inherited ones. That comes down to whether method make() (mb) in B' hides method make() (ma) in A; which comes down to whether signature of mb is a subsignature of ma. ( http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.8 )

The existence of subsignature concept is also to serve the dumbest motivation. Otherwise we only need to worry about same signatures in determining overriding and hiding methods.

But that's not a problem this time. Per definition, mb is not a subsignature of ma, so ma is inherited in class B'. So class B' has two make() methods.

Next step, is to identify potentially applicable methods. The rule says ( http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.2.1 )

If the method invocation includes explicit type parameters, and the member is a generic method, then the number of actual type parameters is equal to the number of formal type parameters.

That means ma is applicable to expression BPrime.<Object>make(), because ma is not a generic method. What?!

The spec explains

The clause above implies that a non-generic method may be potentially applicable to an invocation that supplies explicit type parameters. Indeed, it may turn out to be applicable. In such a case, the type parameters will simply be ignored.

This rule stems from issues of compatibility and principles of substitutability. Since interfaces or superclasses may be generified independently of their subtypes, we may override a generic method with a non-generic one. However, the overriding (non-generic) method must be applicable to calls to the generic method, including calls that explicitly pass type parameters. Otherwise the subtype would not be substitutable for its generified supertype.

So this is also to serve the dumbest motivation, and we have to allow nonsense syntax like

    System.<String,Integer>currentTimeMillis();

Then, both mb and ma are applicable, thus the ambiguity.

like image 149
irreputable Avatar answered Sep 21 '22 21:09

irreputable