Differences in JDK8 javac / Eclipse Luna output type?

I am trying to switch a project to Java8 and run into odd differences between the outputs of Eclipse Luna and Javac. With JDK 1.7.0_65 javac, this code compiles just fine. JDK 1.8.0_11 complains that both toString (char []) and toString (Throwable) match "toString (getKey (code, null)); line. Eclipse Luna 4.4 (I20140606-1215) will compile it with the JDK:

public class TypeInferenceTest { public static String toString(Object obj) { return ""; } public static String toString(char[] ca) { return ""; } public static String toString(Throwable t) { return ""; } public static <U> U getKey(Object code, U defaultValue) { return defaultValue; } public static void test() { Object code = "test"; toString(getKey(code, null)); } } 

I think the only signature that could match is toString (Object).

Of course, I could just add a cast to the object, but I wonder why javac cannot determine the type on its own (while eclipse does), and why heck javac considers Throwable and char [] to be suitable matches, but not Object.

Is this a bug in Eclipse or javac? (I mean, there can only be one compiler here, either it compiles or not)

Edit: error message from javac (JDK8):

 C:\XXXX\Workspace\XXXX\src>javac -cp . TypeInferenceTest.java TypeInferenceTest.java:22: error: reference to toString is ambiguous toString(getKey(code, null)); ^ both method toString(char[]) in TypeInferenceTest and method toString(Throwable) in TypeInferenceTest match 1 error 
+6
source share
2 answers

Compilers can only check method signatures, not the method body, so the part does not matter.

This "reduces" your code to (psuedocode):

 public class TypeInferenceTest { public static String toString(Object obj); public static String toString(char[] ca); public static String toString(Throwable t); public static <U> U getKey(Object code, U defaultValue); public static void test() { Object code = "test"; toString(getKey(code, null)); } } 

Also note that <U> U getKey(...) is actually: <U extends Object> U getKey(...) .

All that is known is that getKey(code, null) returns ? extends Object ? extends Object , therefore returns a subtype of Object or the Object itself.
There are three signatures that match, namely Object , char[] and Throwable , where both char[] and Throwable match the same and better than Object , because you asked ? extends Object ? extends Object .

Therefore, he cannot choose which one is correct, because all three correspond to the signature.

When you change it to:

 public static Object getKey(Object code, Object defaultValue); 

it matches only public static String toString(Object obj); because it better matches any other ? extends Object ? extends Object , which is not equal to Object .

Edit , I looked at the original intent of the question: why does it compile in Java 7, but not in Java 8?

As a result, input like Java 8 has improved significantly.

While in Java 7 it can, for example, conclude that getKey returned Object , now in Java 8 it reports what it returns ? extends Object ? extends Object .

When using Java 7, there was only one match, namely Object .

To visualize the changes even better, consider this piece of code:

 public class TypeInferenceTest { public static String toString(Object obj) { return "1"; } public static String toString(Throwable t) { return "2"; } public static <U> U getKey(Object code, U defaultValue) { return defaultValue; } public static void test() { Object code = "test"; String result = toString(getKey(code, null)); System.out.println(result); } public static void main(String[] args) { test(); } } 

On Java 7 it prints 1 , on Java 8 it prints 2 precisely for the reasons stated above.

+3
source

javac can be really correct. The specification writes :

The null type has a single value, a null reference, represented by the null null literal, which is formed from ASCII characters.

Therefore, the null type is the null type.

The expression getKey(code, null) is an expression of a method call of a generic method. A specification defines its type as follows:

  • If the selected method is general and the invoke method does not provide explicit type arguments, the call type is inferred as specified in ยง18.5.2.

The actual description of the type inference algorithm is pretty involved, but the type assumed for U must be assigned from the null type. Alas, this is true for all reference types, so which one to choose? The most logical is the most specific type, which is the null type. Therefore, the type of the method invocation expression is probably the null type.

Now, to what method does the toString(getKey(code, null)) method call expression apply? The specification writes :

The second step searches for the type defined in the previous step for member methods. At this stage, the method name and argument expressions are used to search for available and applicable methods, i.e. Ads that can be correctly called for the given arguments.

There may be more than one such method, in which case the most specific one is chosen. The handle (type of signature and return) of the most specific method is the one that is used at run time to send the method.

Since the argument type is a null type, all three toString methods apply. The spectrum writes:

A method is considered to be as specific as possible for a method call if it is available and applicable, and there is no other applicable and accessible method that is more specific.

If there is exactly one maximally specific method, then this method is actually the most specific method; it is necessarily more specific than any other available method that is applicable. It then undergoes some additional compile-time checks, as described in 15.12.3.

It is possible that none of the methods is the most specific, since there are two or more methods that are as specific as possible. In this case:

  • If all the most specific methods have equivalently equivalent signatures (ยง8.4.2), then:

    • If exactly one of the most specific methods is specific (that is, not abstract or by default), this is the most specific method.

    • Otherwise, if all the most specific methods are abstract or default, and the signatures of all the most specific methods have the same erasure (ยง4.6), then the most specific method is chosen arbitrarily among the subset of the most specific methods that have the most specific type of return.

      In this case, the most specific method is considered abstract. In addition, it is believed that the most specific method has chosen a checked exception if and only if this exception or its deletion is declared in the throw proposals of each of the most specific methods.

  • Otherwise, the method call is ambiguous and a compile-time error occurs.

Both toString(char[]) and toString(Throwable) more specific than toString(Object) , but no more specific than the others, and their signatures are not equivalent.

Therefore, the method call is ambiguous and rejected by the compiler.

+1
source

Source: https://habr.com/ru/post/972590/


All Articles