使用JDK 11时,我无法理解以下类型安全问题。当我在参数中直接传递Set.of
时,有人能解释一下没有得到编译错误的原因吗
public static void main(String[] args) {
var intSet1 = Set.of(123, 1234, 101);
var strValue = "123";
isValid(strValue, intSet1);// Compilation error (Expected behaviour)
**isValid(strValue, Set.of(123, 1234, 101));// No Compilation error**
}
static <T> boolean isValid(T value, Set<T> range) {
return range.contains(value);
}
您可以在IdeOne.com上实时运行此代码。
简单地说,编译器在第一次调用时使用声明的类型,但在第二次调用时可以推断出兼容的类型。
对于isValid(strValue, intSet1);
,您调用的是isValid(String, Set<Integer>)
,编译器不会将两个参数的T
解析为相同的类型。这就是它失败的原因。编译器根本无法更改您声明的类型。
然而,对于isValid(strValue, Set.of(123, 1234, 101))
,Set.of(123, 1234, 101)
是一个多表达式,其类型是在调用上下文中建立的。因此,编译器的工作是推断上下文中适用的T
。正如Eran所指出的,这就是Serializable
。
为什么第一个有效,第二个无效?这只是因为编译器在作为第二个参数给出的表达式类型方面有一定的灵活性。intSet1
是一个独立的表达式,Set.of(123, 1234, 101)
是一个多表达式(请参阅JLS和关于多表达式的描述(在第二种情况下,上下文允许编译器计算与第一个参数的类型String
兼容的具体T
的类型。
isValid(strValue,Set of(1231234101((
当我把鼠标悬停在Eclipse上的这个isValid()
调用上时,我看到它将执行以下方法:
<Serializable> boolean com.codebroker.dea.test.StringTest.isValid(Serializable value, Set<Serializable> range)
当编译器试图解析用于isValid
的泛型类型参数T
的可能类型时,它需要找到String
(strValue
的类型(和Integer
(Set.of(123, 1234, 101)
的元素类型(的公共超类型,并找到Serializable
。
因此,Set.of(123, 1234, 101)
被解析为Set<Serializable>
而不是Set<Integer>
,因此编译器可以将Serializable
和Set<Serialiable>
传递给isValid()
,这是有效的。
isValid(strValue,intSet1(
另一方面,当您第一次将Set.of(123,1234,101)
分配给变量时,它将解析为Set<Integer>
。在这种情况下,不能将String
和Set<Integer>
传递给isValid()
方法。
如果您更改
var intSet1 = Set.of(123, 1234, 101);
至
Set<Serializable> intSet1 = Set.of(123,1234,101);
然后
isValid(strValue, intSet1);
也将通过编译。
当你(作为一个人(看到编译的第二个isValid
时,可能会想——这怎么可能?类型T
由编译器推断为String
或Integer
,因此调用必须绝对失败。
编译器在查看方法调用时会以完全不同的方式进行思考。它查看方法参数和提供的类型,并试图为您推断出一个完全不同的、不期望的类型。有时,这些类型是";"不可去噪";,意味着编译器可以存在的类型,但作为用户,不能声明这样的类型。
有一个特殊的(未记录的(标志,您可以使用它编译类,并了解编译器";认为":
javac --debug=verboseResolution=all YourClass.java
产量会很长,但我们关心的主要部分是:
instantiated signature: (INT#1,Set<INT#1>)boolean
target-type: <none>
where T is a type-variable:
T extends Object declared in method <T>isValid(T,Set<T>)
where INT#1,INT#2 are intersection types:
INT#1 extends Object,Serializable,Comparable<? extends INT#2>,Constable,ConstantDesc
INT#2 extends Object,Serializable,Comparable<?>,Constable,ConstantDesc
您可以看到,推断和使用的类型不是String
和Integer
。