首页 > 解决方案 > 一个具有泛型返回类型的方法怎么可能分配给该范围之外的变量?

问题描述

假设我有以下结构:

public interface A {
}

public interface B {
}

public interface B1 extends B {
}

public interface B2 extends B {
}

public class C implements A, B1 {
    private final String s;

    public C(final String s) {
        this.s = s;
    }
}

public class D implements A, B2 {
    private final Exception e;

    public D(final Exception e) {
        this.e = e;
    }
}

public class SomeClass<T> {
    private final T t;
    private final Exception e;

    public SomeClass(final T t, final Exception e) {
        this.t = t;
        this.e = e;
    }

    public <U extends B> U transform(final java.util.function.Function<T, ? extends U> mapper1, final java.util.function.Function<Exception, ? extends U> mapper2) {
        return t == null ? mapper2.apply(e) : mapper1.apply(t);
    }
}

现在我们在另一个类中执行以下操作:

public class AnotherClass {
    public static void main(final String[] args) {
        SomeClass<String> someClass = new SomeClass<>("Hello World!", null);
// this line is what is bothering me
        A mappedResult = someClass.transform(C::new, D::new);
    }
}

代码编译没有任何问题。为什么代码会编译?即使方法中的通用 U 被声明为 B 的子类型,“mappedResult”的类型怎么可能是 A?

标签: javagenericsinheritancetype-erasure

解决方案


好的,所以根据对问题的评论和与其他人的一些讨论,我错过了一个可能需要解决的要点,这实际上解释了评论中给出的答案。

很明显,以下编译:

Object mappedResult = someClass.transform(C::new, D::new);

当然,Object 不是 B 的子类。绑定将确保 C 和 D 的类型(在这种情况下)将是 B 的子类型,但由于 C 和 D 都实现了其他接口,它们也可以是其他类型。编译器将检查它们是什么类型,并查看它们共有的最具体的类型。在这种情况下,既是 A 又是 B,因此类型派生为 A & B。因此,可以将此结果分配给 A,因为编译器也会将结果派生为 A。

界限确实提供了一些有关输入的限制,但不涉及输出,也不涉及您可以分配结果的变量类型。这就是我之前困惑的地方。

另一种查看方式如下:如果方法已定义如下:

public <U> U transform(final java.util.function.Function<T, ? extends U> mapper1, final java.util.function.Function<Exception, ? extends U> mapper2) {
    return t == null ? mapper2.apply(e) : mapper1.apply(t);
}

那么在像以前一样调用它时,结果仍然可以分配给 A 或 B。界线对此没有影响。它在这里确保的是两个映射器函数都需要映射到作为 U 的子类型的结果。通过绑定,它成为 U 的子类型,它是 B 的子类型。但是结果是 A 的子类型的事实不会改变它也是 B 的子类型的事实。因此,结果可以分配给任一类型。


推荐阅读