为什么Java泛型超类型的类型推断会出问题?

5

考虑以下这段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却不行。

甚至更简单地解释... - Eugene
2个回答

2

因此,让我们明确地写出我们希望推断的类型。此外,我们将在使用处附近放置声明。

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

2
我假设您了解为什么Optional<SimpleEntry<Integer,String>>不能被赋值给类型为List<Optional<Entry<Integer, String>>>的变量。如果不了解,请阅读Q&A 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语言规范第18.5.2节 "调用类型推断",编译器将通过约束 asList 的返回类型 (List<T>) 限制类型 T 到调用上下文目标类型 (List<Entry<Integer, String>>)。
当编译器选择 TEntry<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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接