这个问题在这里已经有了答案:
为什么这个带绑定的泛型方法可以返回任何类型(1个答案)
上个月关门了。
我们的代码如下:
import java.io.Closeable;
public class Test {
@FunctionalInterface
public interface Action1<E extends Exception> {
void run() throws E;
}
public interface Action2 {
void run();
}
static class MyClass implements Action1<Exception>, Action2 {
@Override
public void run() {
}
}
public static <C extends Action1<Exception> & Action2> C emptyAction() {
return (C) new MyClass();
}
public static void main(String[] args) throws Exception {
// Compiler says: "error: incompatible types: MyClass cannot be converted to Closeable"
// Closeable onClose1 = new MyClass();
// onClose1.close();
// Compiles but ClassCastException at runtime:
// Exception in thread "main" java.lang.ClassCastException: class Test$MyClass cannot be cast to class java.io.Closeable (Test$MyClass is in unnamed module of loader // com.sun.tools.javac.launcher.Main$MemoryClassLoader @62fdb4a6; java.io.Closeable is in module java.base of loader 'bootstrap')
// at Test.main(Test.java:27)
Closeable onClose2 = emptyAction();
onClose2.close();
}
}
注意编译器是如何正确地拒绝直接调用构造函数的( onClose1
),而方法返回类型不会被拒绝,但会继续抛出 ClassCastException
运行时( onClose2
). 我用java11和java14进行了测试,两次都得到了相同的结果。
我不明白为什么编译器接受编译 onClose2
? 泛型类型签名并不表示返回的类型确实是 Closeable
,它也不会创建一个lambda来充当桥。我甚至想知道擦除,但我不知道它在这里可以扮演什么角色。。。
有人有什么解释吗?
1条答案
按热度按时间zvms9eto1#
首先:你正在用界面做一些奇怪的事情。我强烈建议不要让接口具有相同的方法名和只在throws语句中不同的返回类型。默认方法和实现语句的顺序创建了一个自由度。在这两个接口的帮助下,你的合同已经不可靠了。
myclass也不需要实现
@FunctionInterface
. 这就是lambda的用途:自动检查。您应该尝试理解代码中的两个问题:
删除对c的强制转换会导致一个编译器错误,即myclass不能转换为c。
使用所有显式类型运行代码仍然会产生类强制转换异常。
我认为你的代码根本就不应该运行——你已经指出了它的不足
implements Closeable
. 您想了解lambdas和泛型的含义。这绝对超出了简短回答的可能性,但我可以给你两个重要提示:泛型不是继承,因此
List<Number>
永远不会接受Long
. 和一个List<? extends Number>
什么都不接受。具有与java.util.function具有完全相同签名的方法的任何类型(无论是接口还是类)都可以用作lambda表达式中的java.util.function(语言规范§15.27.3)
因此,除了类型参数中的相同字符外,您的c和myclass不共享任何内容。现在该方法建议,任何赋值都将向后传播到myclass,就像您尝试closeable一样。你的意思是:
泛型不能超越经典接口。它们只提供了更详细的表达,比如
List
真的包含。千万不要试图用泛型来表达继承。这就是为什么不能在Map<String,Factory<T>>
作为一个灵活的朋友取决于任务。angelika langer有一系列关于泛型的文章和资源,以及为什么它们不能像你期望的那样工作。我不链接任何特定页面,这与其说是阅读,不如说是一次旅行。
对于lambda,尝试接受java语言规范。我在#2中写了相关段落。根本没有lambda表达式。它也不会修复你的代码。lambda和异常不能一起工作。lambda表达式中没有异常路径,除了要抛出异常的本地/最小上下文。