Java 8 core function should be ambiguous, but runtime failure

I am trying to port Java 7 code to Java 8, so I have code similar to:

package tests; import java.util.Arrays; import java.util.Map; public class Tests { private static interface ComparableMap<K,V> extends Map<K,V>, Comparable {} public static void main(String[] args) { func(getString()); } private static void func(Comparable...input){ System.out.println(Arrays.toString(input)); } private static void func(ComparableMap <?,?> m){ System.out.println(m); } private static <T extends Comparable> T getString(){ return (T) "aaa"; } } 

In java 7 it works correctly, in java 8 I get:

java.lang.ClassCastException: java.lang.String cannot be applied to test.Tests $ ComparableMap

And in case I change one function definition to:

  private static <T> T getString(){ return (T) "aaa"; } 

compilation will fail:

the link to func is ambiguous

Why is the Java 8 compiler not working in the first case? (The error is looking at me) Is it possible to change the second overloaded function to get the first function called with the varargs argument without changing the call itself?

+7
java generics java-8 overload-resolution variadic-functions
source share
2 answers

Compiler error

In the first case, the getString method is required to return an instance of Comparable . The compiler searches for overloads of the func method and finds only one that can accept Comparable : func(Comparable ... input) . Map does not implement this interface, so the second overload is not applicable. There is no ambiguity.

In the second case, getString can return anything. Both overloads work, so there is ambiguity. However, note that casting to T is unsafe / incorrect in both cases.

Personal Area

The type of generic method you wrote basically tells the compiler, "I can return an instance of any class that you need that implements Comparable ." However, you cannot keep this promise.

Suppose I have the following code:

 String str = getString(); Integer num = getString(); 

This code will compile both String and Integer implement the Comparable interface. The second line does not work at runtime: the code tries to pass the String to Integer .

Your second case is also incorrect for the same reason that I mentioned above. It promises can return any desired type. He also cannot keep this promise ( Runnable is a random example, it could be anything):

 Runnable run = getString() 

Your updated code

The compiler sees two possible overloads that match, func(Comparable...input) and func(ComparableMap <?,?> m) . He prefers the latter, because varargs methods are always the last to be chosen ( for reasons of proportionality ). All this is behavioral behavior.

The code then throws a ClassCastException because your getString method doesn't promise (letting the caller decide which type of Comparable to return).

How to fix it?

The main problem is that your getString method makes a false promise. So, I really don't know what this code is trying to execute. If you can clarify, we can help you.

+6
source share

Yes, I have no idea why Java 8 chooses an overload that takes a card over one that accepts Comparable vararg. I'm going to guess that good old erasure is used here.

However, I would just do getString() return a Comparable , not T

-one
source share

All Articles