【问题标题】:Java 8 generic function should be ambiguous, but failing in runtimeJava 8 泛型函数应该是模棱两可的,但在运行时失败
【发布时间】:2017-04-03 20:37:04
【问题描述】:

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

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";
    }
}

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

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

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

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

编译将失败:错误:

对 func 的引用不明确

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

【问题讨论】:

  • @AndrewTobilko 我不认为这是一个正确的重复,因为这里的 OP 在 Java 8 中出现运行时错误,而在可能的重复中它是 Java 8 中的编译错误。
  • 使用原始类型 + 声明返回泛型 T 调用者想要的方法 + 重载方法和可变参数;相当爆炸性的设置。 This question 解释了 Java 8 中的错误,并补充说 func(ComparableMap)func(Comparable...) 更具体。
  • Here 是类似代码的一个示例,在运行时失败并显示ClassCastException。问题是一样的。正如here 解释的那样,推断类型可以是假设的,因为它必须扩展两个接口,并且这种实际实现是否真的存在并不重要。修复很简单:将getString() 更改为private static String getString(){ return "aaa"; }

标签: java generics java-8 variadic-functions overload-resolution


【解决方案1】:

编译器错误

在第一种情况下,方法getString 需要返回一个Comparable 实例。编译器查找func 方法的重载,它只找到一个可以接受Comparable 的方法:func(Comparable ... input)Map 没有实现那个接口,所以第二个重载不适用。没有歧义。

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

usafe 演员表

您编写的泛型方法基本上告诉编译器“我可以返回您想要实现Comparable 的任何类的实例”。但是,您实际上不能信守诺言。

假设我有以下代码:

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

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

由于我上面解释的相同原因,您的第二种情况也是错误的。它承诺它可以返回你想要的任何类型。它显然也无法兑现承诺(Runnable 这是一个随机的例子,它可以是任何东西):

Runnable run = getString()

您的更新代码

编译器看到两个可能的重载都匹配,func(Comparable...input)func(ComparableMap &lt;?,?&gt; m)。它更喜欢第二个,因为 varargs 方法总是最后选择(for compatebility reasons)。所有这些都是正确的行为。

然后代码抛出 ClassCastException,因为您的 getString 方法没有兑现承诺(让调用者决定返回哪种 Comparable)。

如何解决?

根本问题是您的getString 方法做出了错误的承诺。因此,我真的不知道该代码试图完成什么。如果您能详细说明,我们或许可以为您提供更多帮助。

【讨论】:

  • 查看更新的代码,现在地图也可比,仍然保持不变。我正在尝试完成以下操作之一:获取编译器错误或让代码像 Java 7 一样工作。
  • 好的,让我考虑一下您更新的代码。我的观点是 Java 7 代码是错误的,没有办法让它“像在 Java 7 中一样工作”。编译器在 Java 8 中变得更智能了,所以它现在对这样的错误代码的行为有所不同。
  • 我添加了一个部分来解释为什么您的更新代码也会失败。
  • @AlexanderSlepoy 如果您觉得我的回答对您有所帮助,您可能需要单击此回答旁边的绿色复选标记以表明您的问题已解决。
【解决方案2】:

是的,我不知道为什么 Java 8 选择采用映射的重载而不是采用 Comparable 可变参数的重载。我猜想这里是很好的旧类型擦除。

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

【讨论】:

    猜你喜欢
    • 2010-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-28
    相关资源
    最近更新 更多