为什么 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);
}
}
初始化 list
、list2
和 list3
的表达式工作正常。但是,初始化 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
的类型推断在适用于 list1
到 list3
时会中断(就此而言,也适用于对 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
声明有效。
list1
和 list4
声明之间存在差异。对于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",编译器将asList
(List<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);
鉴于此 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);
}
}
初始化 list
、list2
和 list3
的表达式工作正常。但是,初始化 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
的类型推断在适用于 list1
到 list3
时会中断(就此而言,也适用于对 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
声明有效。
list1
和 list4
声明之间存在差异。对于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",编译器将asList
(List<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);