为什么简单的“捕获”?不编译甚至可以在编译时推断出类型安全?

Why simple “capture of ?” does not compile even type-safety could be compile-time inferred?

我有一个 class 具有严格、简单的通用类型:

public class GenericTools<T> {

    private final Supplier<T> supplier;
    private final Consumer<T> consumer;

    public GenericTools(Supplier<T> supplier, Consumer<T> consumer) {
        this.supplier = supplier;
        this.consumer = consumer;
    }

    public Supplier<T> getSupplier() {
        return supplier;
    }

    public Consumer<T> getConsumer() {
        return consumer;
    }
}

这里不能使用"capture of ?",文件编译不通过的具体原因是什么?

GenericTools<?> tools = new GenericTools<>(Math::random, System.out::println);
tools.getConsumer().accept(tools.getSupplier().get());

Error:(27, 59) java: incompatible types: java.lang.Object cannot be converted to capture#1 of ?

使用显式 <Double> 编译没有问题:

GenericTools<Double> tools = new GenericTools<>(Math::random, System.out::println);
tools.getConsumer().accept(tools.getSupplier().get());

我已经用Java1.8编译了

请注意,这完全不是 的重复,发帖人不知道我们需要在代码中传递什么作为“?”类型的参数需要它。就我而言,我非常了解捕获机制,但仍然无法使用看起来应该工作的类型。最重要的是,我在这里问的是确切原因(规范参考或其他),而不是我应该通过什么

Generics tutorial中所写:

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error

Since we don't know what the element type of c stands for, we cannot add objects to it. The add() method takes arguments of type E, the element type of the collection. When the actual type parameter is ?, it stands for some unknown type. Any parameter we pass to add would have to be a subtype of this unknown type. Since we don't know what type that is, we cannot pass anything in. The sole exception is null, which is a member of every type.

On the other hand, given a List<?>, we can call get() and make use of the result. The result type is an unknown type, but we always know that it is an object. It is therefore safe to assign the result of get() to a variable of type Object or pass it as a parameter where the type Object is expected.

这相当于

GenericTools<?> tools = ...
tools.getConsumer().accept(new Object()); //or tools.getSupplier().get() which returns Object

一旦你声明了你的泛型类型,这就是编译器在其余调用中所知道的。 getConsumer().accept() 将不得不接受 ?(但它不能)并且 getSupplier().get() 将不得不 return Object.

当声明 GenericTools<Double> 时,您会保留类型信息,并且 getConsumer().accept()getSupplier().get() 都知道它们正在使用 Double

因为?类型是不变的,Object不是?范围内所有类型的子类型。

我相信 Java 8 类型推断能够推断 T 在 RHS 上是 Double,但是由于您明确分配给 GenericTools<?> LHS,捕获是一个无界类型变量,它与无界变量T统一,无界变量T也没有边界。

没有任何界限,Supplier::getConsumer::accept的签名中的T不保证是相同的类型——请记住,类型变量是不变的,因为没有表达协变或逆变界限。 Supplier 侧的 T 擦除只是 Object,编译器无法插入运行时检查运行时类型实际上是 ?(因为 ? 不可具体化!)。因此:类型Object无法隐式转换为?,编译失败。