JDK 11 泛型使用 Set.of 时出现的问题

2022-09-02 20:26:23

在使用 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 实时运行此代码。


答案 1

简单地说,编译器在第一次调用时就坚持使用您声明的类型,但在第二次调用上推断出兼容类型的自由度。

使用 ,您正在调用 ,并且编译器不会将两个参数解析为相同的类型。这就是它失败的原因。编译器根本无法更改已声明的类型。isValid(strValue, intSet1);isValid(String, Set<Integer>)T

但是,with 是一个 poly 表达式,其类型是在调用上下文中建立的。因此,编译器的工作是推断出在上下文中适用的。正如Eran指出的那样,这是.isValid(strValue, Set.of(123, 1234, 101))Set.of(123, 1234, 101)TSerializable

为什么第一个有效,第二个不起作用?这仅仅是因为编译器在作为第二个参数给出的表达式类型方面具有一定的灵活性。 是一个独立的表达式,并且是一个 poly 表达式(请参阅 JLS这个关于 poly 表达式的描述)。在第二种情况下,上下文允许编译器计算一个类型,该类型的工作具体与 第一个参数的类型兼容。intSet1Set.of(123, 1234, 101)TString


答案 2

isValid(strValue, Set.of(123, 1234, 101));

当我将鼠标悬停在Eclipse上的这个调用上时,我看到它将执行以下方法:isValid()

<Serializable> boolean com.codebroker.dea.test.StringTest.isValid(Serializable value, Set<Serializable> range)

当编译器尝试解析可能用于泛型类型参数的类型时,它需要找到一个通用的超类型(类型)和(元素类型),并找到 。TisValidStringstrValueIntegerSet.of(123, 1234, 101)Serializable

因此被解析为 而不是 ,因此编译器可以将 a 和 a 传递给 ,这是有效的。Set.of(123, 1234, 101)Set<Serializable>Set<Integer>SerializableSet<Serialiable>isValid()

isValid(strValue, intSet1);

另一方面,当您第一次赋值变量时,它会解析为 .在这种情况下,a 和 a 不能传递给你的方法。Set.of(123,1234,101)Set<Integer>StringSet<Integer>isValid()

如果您更改

var intSet1 = Set.of(123, 1234, 101);

Set<Serializable> intSet1 = Set.of(123,1234,101);

然后

isValid(strValue, intSet1);

也会通过编译。


推荐