Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8: Reference to [method] is ambiguous [duplicate]

Does anybody understand why the following code will compile fine in Java 7 and below, but fails with Java 8.

public static void main(String[] args) throws Exception {     put(get("hello")); }  public static <R> R get(String d) {     return (R)d; }  public static void put(Object o) {     System.err.println("Object " + o); }  public static void put(CharSequence c) {     System.err.println("CharSequence " + c); }  public static void put(char[] c) {     System.err.println("char[] " + c); } 

The get method has a generic return type. In JDK 7 and below this compiles fine and the put method with the Object parameter is chosen. In JDK 8 this cannot be compiled, indicating the put method is ambiguous.

Apparently JDK 8 is skipping over the Object-parameter method and finding the last two sub-Object-parameter methods and complaining about them (i.e. if you add another put method with some other parameter type, the compiler will switch and complain about the new last two methods)

This seems like a bug.

like image 289
Ken Avatar asked Aug 30 '15 07:08

Ken


People also ask

What is ambiguous method call in Java?

This ambiguous method call error always comes with method overloading where compiler fails to find out which of the overloaded method should be used.

What is ambiguity problem in Java how to overcome ambiguity problem in Java and give reason for that?

Ambiguity errors occur when erasure causes two seemingly distinct generic declarations to resolve to the same erased type, causing a conflict. Here is an example that involves method overloading. In this case, Java uses the type difference to determine which overloaded method to call.

What is ambiguity error in method overloading?

There are ambiguities while using variable arguments in Java. This happens because two methods can definitely be valid enough to be called by data values. Due to this, the compiler doesn't have the knowledge as to which method to call.

What is ambiguity problem?

Ambiguity is a type of meaning in which a phrase, statement or resolution is not explicitly defined, making several interpretations plausible. A common aspect of ambiguity is uncertainty.


2 Answers

Your problem is a side-effect of Generalized Target-type Inference, an improvement in Java 8.

What is Target-type Inference

Let's take your example method,

public static <R> R get(String d) {     return (R)d; } 

Now, in the method above, the generic parameter R cannot be resolved by the compiler because there's no parameter with R.

So, they introduced a concept called Target-type Inference, which allows the parameter to be inferred based on the assignment parameter.

So, if you do,

 String str = get("something"); // R is inferred as String here  Number num = get("something"); // R is inferred as Number here 

This works well in Java 7. But the following does not,

put(get("something"); static void Put(String str) {} //put method 

Because type inference worked only for direct assignments.

If there's no direct assignment, then the generic type was inferred as Object.

So, when you compiled the code with Java 7, your put(Object) method was called without any problems.

What they did in Java 8

They improved the type inference to infer the type from method calls and chained method calls

More details about them here and here

So now, you can directly call put(get("something")) and the generic type will be inferred based on the parameter of the put() method.

But as you know, the methods, put(Charsequence) and put(char[]) match the arguments. So there's the ambiguity.

Fix?

Just tell the compiler exactly what you want,

put(TestClass.<CharSequence>get("hello")); // This will call the put(CharSequence) method. 
like image 135
Codebender Avatar answered Sep 26 '22 12:09

Codebender


Looks like this is a known incompatibility.

See "Area: Tools / javac" section of this article. And this bug.

Synopsis

The following code which compiled, with warnings, in JDK 7 will not compile in JDK 8:

import java.util.List;   class SampleClass {       static class Baz<T> {         public static List<Baz<Object>> sampleMethod(Baz<Object> param) {             return null;         }     }       private static void bar(Baz arg) {         Baz element = Baz.sampleMethod(arg).get(0);     } }  

Compiling this code in JDK 8 produces the following error:

SampleClass.java:12: error:incompatible types: Object cannot be converted to Baz     Baz element = Baz.sampleMethod(arg).get(0);                                            Note: SampleClass.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details. 1 error  

In this example, a raw type is being passed to the sampleMethod(Baz<Object>) method which is applicable by subtyping (see the JLS, Java SE 7 Edition, section 15.12.2.2).

An unchecked conversion is necessary for the method to be applicable, so its return type is erased (see the JLS, Java SE 7 Edition, section 15.12.2.6). In this case the return type of sampleMethod(Baz<Object>) is java.util.List instead of java.util.List<Baz<Object>> and thus the return type of get(int) is Object, which is not assignment-compatible with Baz.

like image 42
Amila Avatar answered Sep 25 '22 12:09

Amila