【问题标题】:Why does Java type inference for generic supertypes break here?为什么泛型超类型的 Java 类型推断会在此处中断?
【发布时间】:2019-11-20 01:44:02
【问题描述】:

鉴于此 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。那么,当 list1list3 工作时,为什么 list4 的类型推断会中断(同样也适用于对 optionalEntry 的分配)?

尤其是我不明白为什么分配给list1 的工作有效,而分配给list4 的工作却无效。

【问题讨论】:

标签: java generics type-inference


【解决方案1】:

我假设您理解为什么不能将Optional&lt;SimpleEntry&lt;Integer,String&gt;&gt; 分配给List&lt;Optional&lt;Entry&lt;Integer, String&gt;&gt;&gt; 类型的变量。如果没有,请阅读问答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&lt;Integer, String&gt;simpleEntry 的类型)。

根据Java Language Specification, section 18.5.2, "Invocation Type Inference",编译器会通过将asListList&lt;T&gt;)的返回类型约束为调用上下文目标类型(List&lt;Entry&lt;Integer, String&gt;&gt;)来进一步约束T类型。

这是可能的;当编译器选择 T 为 Entry&lt;Integer, String&gt; 时,整个表达式都适合,因为 SimpleEntry&lt;Integer, String&gt; 类型的值可以分配给 Entry&lt;Integer, String&gt; 类型的变量。

对于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&lt;SimpleEntry&lt;Integer, String&gt;&gt;。表达式上下文的目标类型是List&lt;Optional&lt;Entry&lt;Integer, String&gt;&gt;&gt;。编译器不可能提出适合两者的T

Optional&lt;SimpleEntry&lt;Integer, String&gt;&gt; 类型的值不能分配给Optional&lt;Entry&lt;Integer, String&gt;&gt;&gt; 类型的变量。

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

简单来说

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

你可以说

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

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

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

【讨论】:

    【解决方案2】:

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

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

    SimpleEntry&lt;xyz&gt;Entry&lt;xyz&gt;,所以没关系。

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

    Optional&lt;xyz&gt; 是平凡的Optional&lt;xyz&gt;

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

    Optional&lt;xyz&gt; 又是一个 Optional&lt;xyz&gt;

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

    阿沃加! Optional&lt;SimpleEntry&lt;xyz&gt;&gt; 不是 Optional&lt;Entry&lt;xyz&gt;&gt;

    你可以使用Optional&lt;? extends Entry&lt;xyz&gt;&gt;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-16
      • 2018-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多