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

kkbh8khc  于 2021-07-08  发布在  Java
关注(0)|答案(1)|浏览(267)

这个问题在这里已经有了答案

为什么这个带绑定的泛型方法可以返回任何类型(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来充当桥。我甚至想知道擦除,但我不知道它在这里可以扮演什么角色。。。
有人有什么解释吗?

zvms9eto

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一样。你的意思是:

public interface SuperInterface extends Action1, Action2 {
}

public static SuperInterface emptyAction() {
   return new MyClass();
}

泛型不能超越经典接口。它们只提供了更详细的表达,比如 List 真的包含。千万不要试图用泛型来表达继承。这就是为什么不能在 Map<String,Factory<T>> 作为一个灵活的朋友取决于任务。
angelika langer有一系列关于泛型的文章和资源,以及为什么它们不能像你期望的那样工作。我不链接任何特定页面,这与其说是阅读,不如说是一次旅行。
对于lambda,尝试接受java语言规范。我在#2中写了相关段落。根本没有lambda表达式。它也不会修复你的代码。lambda和异常不能一起工作。lambda表达式中没有异常路径,除了要抛出异常的本地/最小上下文。

相关问题