【问题标题】:Why returned function needs additional casting (incompatible types)?为什么返回的函数需要额外的转换(不兼容的类型)?
【发布时间】:2017-04-27 05:59:39
【问题描述】:

有一个类 A 可能会以某种方式映射到另一个类 B 或其他类:

class A {}

class B {
    final A a;

    B(A a) {
        this.a = a;
    }
}

还有一个映射器工厂,它根据作为参数传递的第二个类类型将映射器从 A 返回到另一个类:

class Mapper {

    static Function<A, B> a2bmapper = B::new;

    static <R> Function<A, R> findMapper(Class<R> cls) {
        if(cls == B.class) {
            return a2bmapper;
        }
        return null;
    }        
}

问题在于这一行:

return a2bmapper;

java 编译器问题类型不兼容:Required RFound B 和 IDE 建议转换为 Function&lt;A,R&gt;。这是为什么? R 只是一个泛型类型,应该替换为B

【问题讨论】:

  • 简短的回答是类型系统可以检查很多,但不能检查所有内容。限制(理论和实践,就语言规范和编译器投入的时间和精力而言)开始发挥作用。在这种情况下,Java 不会进行传递计算,因为 cls == B.class, R == B.

标签: java function generics lambda java-8


【解决方案1】:

findMapper 应该返回Function&lt;A, R&gt;,其中R 可以是任何东西,不一定是B

假设我们用String.class 调用这个方法。现在RString。该函数应该返回一个Function&lt;A, String&gt;,但您却返回了一个Function&lt;A, B&gt;。编译器看到了这种可能性,对你说不。

“但我在返回之前检查了R 是否为B!”你喊道。好吧,该检查是在运行时完成的,编译器并不关心。

由于类型擦除,每个泛型参数在运行时只是Object。这就是为什么您可以将其转换为 Function&lt;A, B&gt; 来解决此问题。

【讨论】:

    【解决方案2】:

    虽然从语义上讲,当您传入 B.class 时,此方法无法返回除 Function&lt;A,B&gt; 之外的任何内容,但编译器不够聪明,无法实现这一点。例如,对if 条件的错误更改就足以破坏您的语义。在这类情况下,JLS 通常会谨慎行事。

    在这种情况下,您需要显式转换为 Function&lt;A,R&gt; 才能按照您的意愿行事。

    【讨论】:

      【解决方案3】:

      泛型在 Java 中纯粹是编译时的事情。编译器使用泛型来检查你的代码是否是类型安全的,当然是在编译时。

      但是编译器所做的检查是有限制的。编译器的检查并没有走得太远以至于编译器将分析if 语句以得出结论,在return 语句处,R 始终等于B

      如果它比一个if 语句更复杂怎么办?您仍然希望编译器分析代码中所有可能的路径并得出结论认为它是安全的吗?逻辑可能会变得任意复杂。

      【讨论】:

        【解决方案4】:

        对不起,我的英语不好,所以我尽可能多地举例子。我希望你能明白我的意思。

        举个简单的例子,参数类型是一个对象。

        String string(Object value){
          return value instanceof String ? value : null; 
        }
        

        上面的例子仍然需要向下转换String,因为value的引用类型是Object

        String string(Object value){
          return value instanceof String ? (String)value : null; 
        }
        

        AND 然后我们使用通用参数扩展示例,您可以通过强制将String 强制转换为T 的类型来解决方法表达式语句分配的有界。如果有界 T 不是派生自 String 类的类型,则在运行时强制转换将失败。

        <T,R> T string(R value){
            // the code cast R to T will generate a compile unchecked warnings.
            return value instanceof String ? (T) value : null;
        }
        
        // the code is ok on compile stage, but will throw a ClassCastException on runtime.
        Date date= string("bad");
        // the code is ok both on compile & runtime.
        // because a unbounded generic argument which will reference to Object .
        string("ok");
        

        根据上面的例子,你可以通过将函数转换为Function&lt;T,R&gt;来解决你的代码:

        static <R> Function<A, R> findMapper(Class<R> cls) {
            if (cls == B.class) {
                return (Function<A, R>) a2bmapper;
            }
            return null;
        }
        

        我们知道泛型参数RB,但由于编译器不知道,仍然会生成未经检查的编译警告。然后我们可以做以下让编译器知道它:

        static <R> Function<A, R> findMapper(Class<R> cls) {
            if (cls == B.class) {
                return a2bmapper.getClass().cast(a2bmapper);
            }
            return null;
        }
        

        【讨论】:

          猜你喜欢
          • 2015-06-25
          • 2014-04-09
          • 2011-04-13
          • 1970-01-01
          • 2020-02-19
          • 1970-01-01
          • 2016-01-19
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多