为什么返回函数需要额外的转换(不兼容的类型)?

时间:2017-04-27 05:59:39

标签: java function generics lambda java-8

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

class A {}

class B {
    final A a;

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

还有一个mapper工厂,它根据作为参数传递的第二个类类型将映射器从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编译器发出不兼容的类型:必需R 找到B ,IDE建议转换为Function<A,R>。这是为什么? R只是一种通用类型,应替换为B

4 个答案:

答案 0 :(得分:5)

findMapper应该返回Function<A, R>,其中R可以是任何内容,不一定是B

我们假设我们用String.class调用此方法。现在RString。该函数应返回Function<A, String>,但您将返回Function<A, B>。编译器看到了这种可能性并对你说不。

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

由于类型擦除,每个泛型参数在运行时只是Object。这就是为什么你可以把它投射到Function<A, B>来解决这个问题。

答案 1 :(得分:4)

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

在这种情况下,您需要对Function<A,R>进行显式转换才能按照您的意愿进行操作。

答案 2 :(得分:3)

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

但是编译器的检查有限制。编译器确实没有进行检查,以至于编译器将分析if语句以得出结论:在return语句时,R总是等于{{ 1}}。

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

答案 3 :(得分:0)

我对自己糟糕的英语感到抱歉,所以我尽可能多地举例说明。我希望你能理解我的意思。

让我们举个参数类型为Object的简单示例。

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 然后我们使用泛型参数扩展示例,您可以通过强制转换StringT类型来解决,其中方法表达式语句已分配。如果有界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<T,R>来解决您的代码:

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