为什么javac在方法的返回类型上使用有界类型泛型编译这段代码?这是无效的类型推断吗?

为什么javac在方法的返回类型上使用有界类型泛型编译这段代码?这是无效的类型推断吗?,java,generics,type-inference,type-bounds,Java,Generics,Type Inference,Type Bounds,我们的代码与以下类似: import java.io.Closeable; 公开课考试{ @功能接口 公共接口操作1{ void run()抛出E; } 公共接口操作2{ 无效运行(); } 静态类MyClass实现Action1、Action2{ @凌驾 公开募捐{ } } 公共静态C清空操作(){ 返回(C)新的MyClass(); } 公共静态void main(字符串[]args)引发异常{ //编译器说:“错误:不兼容的类型:MyClass无法转换为Closeable” //Close

我们的代码与以下类似:

import java.io.Closeable;
公开课考试{
@功能接口
公共接口操作1{
void run()抛出E;
}
公共接口操作2{
无效运行();
}
静态类MyClass实现Action1、Action2{
@凌驾
公开募捐{
}
}
公共静态C清空操作(){
返回(C)新的MyClass();
}
公共静态void main(字符串[]args)引发异常{
//编译器说:“错误:不兼容的类型:MyClass无法转换为Closeable”
//Closeable onClose1=新MyClass();
//onClose1.close();
//在运行时编译但不编译ClassCastException:
//线程“main”java.lang.ClassCastException中的异常:无法将类Test$MyClass转换为类java.io.Closeable(Test$MyClass位于loader//com.sun.tools.javac.launcher.main$MemoryClassLoader@62fdb4a6的未命名模块中;java.io.Closeable位于loader'bootstrap'的模块java.base中)
//at Test.main(Test.java:27)
Closeable onClose2=emptyAction();
onClose2.close();
}
}
请注意,编译器如何正确地拒绝直接调用构造函数(
onClose1
),而方法返回类型不会被拒绝,但会在运行时继续抛出
ClassCastException
)。我用Java11和Java14进行了测试,两次都得到了相同的结果

我不明白为什么编译器接受编译
onClose2
?泛型类型签名并不表示返回的类型确实是可关闭的,它也不会创建一个lambda来充当桥接器。我甚至想知道擦除,但我不知道它在这里扮演什么角色


有人有什么解释吗?

首先:你在用界面做一些奇怪的事情。我强烈建议不要让接口具有相同的方法名和返回类型,只在抛出语句中有所不同。默认方法和implements语句的顺序创建了一个自由度。您在这两个接口的帮助下表达的合同不再可靠

MyClass也不需要实现
@FunctionInterface
。这就是lambda的用途:自动检查

您应该尝试理解代码中的两个问题:

  • 删除硬转换到C会导致编译器错误,MyClass无法转换到C
  • 使用所有显式类型运行代码仍然会产生类强制转换异常
  • 我认为你的代码根本不应该运行——你已经指出了
    实现Closeable
    的不足。您想了解lambdas和泛型的含义。这绝对不可能是一个简短的答案,但我可以给你两个重要提示:


  • 泛型不是继承,因此
    列表
    永远不会接受
    。谢谢你花时间回答。这些接口的每一个变体都有存在的理由。我宁愿要一个,但那永远不会发生,所以我对它保持平静。“泛型不能超越经典接口。永远不要试图用泛型来表达继承。”谢谢,这在使用泛型时非常有用。我现在可以看到我在哪里犯了错误,以及为什么我需要为这样的用例检查
    超级接口。安吉丽卡·兰格的网站是一个很好的资源!非常感谢你!既然您采取了这一步,我想提出另一种观点:泛型只在作业的左侧对您有帮助。例如,
    列表
    列表
    总是可以分配给
    列表类型的变量,我想我明白你的意思了。对于泛型,
    Long
    Double
    不是
    Number
    的有效替代品,但它们是
    ?扩展数字
    。抱歉,也许我没有使用正确的术语。所以实际上,它看起来像是继承和Liskov替换原理,但实际上,它不一样,有更多的微妙之处。
    public interface SuperInterface extends Action1, Action2 {
    }
    
    public static SuperInterface emptyAction() {
       return new MyClass();
    }