Java 8generics函数应该是不明确的,但在运行时失败

我正在尝试将Java 7代码迁移到Java 8,所以我的代码类似于:

package tests; import java.util.Arrays; import java.util.Map; public class Tests { private static interface ComparableMap extends Map, 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 getString(){ return (T) "aaa"; } } 

在java 7中它正常工作,在java 8中我得到:

java.lang.ClassCastException:java.lang.String无法强制转换为tests.Tests $ ComparableMap

如果我将一个函数定义更改为:

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

编译将失败:错误:

对func的引用含糊不清

为什么Java 8编译器在第一种情况下没有失败? (看起来对我来说是错误的)是否可以更改第二个重载函数,以便在不更改调用本身的情况下使用varargs参数调用第一个函数?

编译错误

在第一种情况下,需要方法getString来返回Comparable实例。 编译器查找func方法的重载,它只找到一个可以接受Comparablefunc(Comparable ... input)Map没有实现该接口,因此第二个重载不适用。 没有含糊之处。

在第二种情况下, getString可以返回任何内容。 这两个重载都有效,因此存在歧义。 但请注意,在这两种情况下,对T的强制转换是不安全/错误的。

USAfe演员

你编写的generics方法基本上告诉编译器“我可以返回你想要实现Comparable的任何类的实例”。 但你实际上不能遵守这个承诺。

可以说我有以下代码:

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

这段代码将编译, StringInteger实现了Comparable接口。 第二行在运行时会失败:代码尝试将String转换为Integer

你的第二个案例也是错误的,原因与我上面解释的相同。 它承诺它可以返回任何你想要的类型 。 它似乎也不能保持这个承诺( Runnable这里是一个随机的例子,它可以是任何东西):

 Runnable run = getString() 

您更新的代码

编译器看到两个可能的重载,它们都匹配, func(Comparable...input)func(ComparableMap m) 。 它更喜欢第二个,因为varargs方法总是最后选择( 出于竞争性原因 )。 所有这些都是核心行为。

然后代码抛出ClassCastException,因为你的getString方法没有保证它的承诺(让调用者决定返回什么样的Comparable )。

怎么解决?

根本问题是你的getString方法犯了一个假承诺。 因此,我真的不知道该代码试图完成什么。 如果你能详细说明,我们可以帮助你进一步发展。

是的,我不知道为什么Java 8会选择带有映射的重载,这个映射会使用Comparable vararg。 我猜这个好的老式擦除在这里发挥作用。

也就是说,我只是让getString()返回一个Comparable而不是T