为什么泛型超类型的Java类型推断会在此处中断?

时间:2019-11-20 01:44:02

标签: java generics type-inference

给出以下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。那么,为什么在list4list1的情况下,list3的类型推断会中断(以及为此分配给optionalEntry的分配)?

尤其是我不明白为什么对list1的分配不起作用时对list4的分配起作用。

2 个答案:

答案 0 :(得分:4)

我假设您了解为什么无法将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);

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

根据Java Language Specification, section 18.5.2, "Invocation Type Inference",编译器将通过将TasList)的返回类型限制为调用上下文目标类型({ {1}}。

这是可能的;当编译器选择T为List<T>时,整个表达式就适合了,因为类型List<Entry<Integer, String>>的值可以分配给类型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>>>

答案 1 :(得分:3)

因此,让我们明确地编写我们期望推断的类型。另外,我们将声明放在使用位置附近。

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

Awooga! Optional<SimpleEntry<xyz>>不是Optional<Entry<xyz>>

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