为什么 Java 泛型超类型的类型推断会在这里中断?

Why does Java type inference for generic supertypes break here?

鉴于此 Java 代码:

import java.util.AbstractMap.SimpleEntry;
import java.util.Arrays;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;

public class Test {
    public static void main(String[] args) {
        SimpleEntry<Integer, String> simpleEntry = new SimpleEntry<>(1, "1");
        Optional<Entry<Integer, String>> optionalEntry = Optional.of(simpleEntry);
        Optional<SimpleEntry<Integer, String>> optionalSimpleEntry = Optional.of(simpleEntry);

        List<Entry<Integer, String>> list1 = Arrays.asList(simpleEntry);
        List<Optional<Entry<Integer, String>>> list2 = Arrays.asList(optionalEntry);
        List<Optional<SimpleEntry<Integer, String>>> list3 = Arrays.asList(optionalSimpleEntry);
        List<Optional<Entry<Integer, String>>> list4 = Arrays.asList(optionalSimpleEntry);
    }
}

初始化 listlist2list3 的表达式工作正常。但是,初始化 list4 的表达式在 Eclipse 中因以下错误而中断:

Type mismatch: cannot convert from List<Optional<AbstractMap.SimpleEntry<Integer,String>>>
to List<Optional<Map.Entry<Integer,String>>>

javac 中出现此错误:

Test.java:16: error: incompatible types: inference variable T has incompatible bounds
        List<Optional<Entry<Integer, String>>> list4 = Arrays.asList(optionalSimpleEntry);
                                                                    ^
    equality constraints: Optional<Entry<Integer,String>>
    lower bounds: Optional<SimpleEntry<Integer,String>>
  where T is a type-variable:
    T extends Object declared in method <T>asList(T...)

但是AbstractMap.SimpleEntry直接实现了Map.Entry。那么,为什么 list4 的类型推断在适用于 list1list3 时会中断(就此而言,也适用于对 optionalEntry 的赋值)?

特别是我不明白为什么 list1 的赋值有效而 list4 的赋值无效。

所以让我们显式地写出我们期望推断的类型。此外,我们会将声明放在使用附近。

SimpleEntry<Integer, String> simpleEntry = ...
List<Entry<Integer, String>> list1 =
    Arrays.<Entry<Integer, String>>asList(simpleEntry);

SimpleEntry<xyz> 是一个 Entry<xyz> 所以没关系。

Optional<Entry<Integer, String>> optionalEntry = ...
List<Optional<Entry<Integer, String>>> list2 =
    Arrays.<Optional<Entry<Integer, String>>>asList(optionalEntry);

Optional<xyz> 是平凡的 Optional<xyz>.

Optional<SimpleEntry<Integer, String>> optionalSimpleEntry = ...
List<Optional<SimpleEntry<Integer, String>>> list3 =
    Arrays.<Optional<SimpleEntry<Integer, String>>>asList(optionalSimpleEntry);

Optional<xyz> 又是一个 Optional<xyz>

Optional<SimpleEntry<Integer, String>> optionalSimpleEntry = ...
List<Optional<Entry<Integer, String>>> list4 =
    Arrays.<Optional<Entry<Integer, String>>>asList(optionalSimpleEntry);

哇哦! Optional<SimpleEntry<xyz>> 不是 Optional<Entry<xyz>>.

你可以使用 Optional<? extends Entry<xyz>>

我假设您理解为什么 Optional<SimpleEntry<Integer,String>> 不能分配给类型 List<Optional<Entry<Integer, String>>> 的变量。如果没有,请阅读问答 Is List a subclass of List? Why are Java generics not implicitly polymorphic?

但是,您的问题是为什么 list1 声明有效,但 list4 声明有效。

list1list4 声明之间存在差异。对于list1,形式为:

SimpleEntry<Integer, String> simpleEntry = ...;
List<Entry<Integer, String>> list = Arrays.asList<T>(simpleEntry);

在这种情况下,Arrays.asList 方法的类型变量 T 尚未固定为特定类型。它的上限为 SimpleEntry<Integer, String>simpleEntry 的类型)。

根据Java Language Specification, section 18.5.2, "Invocation Type Inference",编译器将asListList<T>)的return类型进一步约束到T类型调用上下文目标类型 (List<Entry<Integer, String>>).

这是可能的;当编译器选择 T 为 Entry<Integer, String> 时,整个表达式适合,因为类型 SimpleEntry<Integer, String> 的值可以分配给类型 Entry<Integer, String>.

的变量

对于list4,形式为:

SimpleEntry<Integer, String> simpleEntry = new SimpleEntry<>(1, "1");
Optional<SimpleEntry<Integer, String>> optionalSimpleEntry = Optional.of(simpleEntry);
List<Optional<Entry<Integer, String>>> list4 = Arrays.asList<T>(optionalSimpleEntry);

这里,T 最初被限制在 Optional<SimpleEntry<Integer, String>> 的上限。表达式上下文的目标类型是 List<Optional<Entry<Integer, String>>>。编译器不可能想出一个适合两者的T

Optional<SimpleEntry<Integer, String>> 类型的值不能分配给 Optional<Entry<Integer, String>>> 类型的变量。

这就是编译器抱怨的原因。

简单来说

简单来说,对于泛型不受约束的方法,并且存在约束泛型的表达式上下文,它适用于一层深度的参数化。

你可以说

Dog dog = ...;
List<Animal> animals = Arrays.asList(dog);

但它在更深层次的参数化上不起作用。

Optional<Dog> optionalDog = ...;
List<Optional<Animal>> optionalAnimals = Arrays.asList(optionalDog);