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

Java JDK 11泛型在使用Set.of时出现问题,java,generics,openjdk-11,static-factory,Java,Generics,Openjdk 11,Static Factory,在使用JDK 11时,我无法理解以下类型安全问题。当我在参数中直接传递的集合时,有人能解释没有得到编译错误的原因吗: public static void main(String[] args) { var intSet1 = Set.of(123, 1234, 101); var strValue = "123"; isValid(strValue, intSet1);// Compilation error (Expected behaviour)

在使用JDK 11时,我无法理解以下类型安全问题。当我在参数中直接传递的
集合时,有人能解释没有得到编译错误的原因吗:

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);
}
publicstaticvoidmain(字符串[]args){
var intSet1=一组(123、1234、101);
var strValue=“123”;
isValid(strValue,intSet1);//编译错误(预期行为)
**isValid(strValue,Set.of(12312341301));//无编译错误**
}
静态布尔值有效(T值,设置范围){
返回范围.contains(值);
}
您可以运行它。

是有效的(strValue,Set.of(12312341301))

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

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


也将通过编译。

简单地说,编译器在第一次调用时使用声明的类型,但在第二次调用时可以推断兼容的类型

isValid(strValue,intSet1)
,您正在调用
isValid(String,Set)
,编译器没有将
T
解析为两个参数的相同类型。这就是它失败的原因。编译器无法更改您声明的类型

然而,
Set.of(12312341301)
是一个poly表达式,其类型在调用上下文中建立。因此,编译器可以推断出适用于上下文的
T
。正如Eran指出的,这是可序列化的

为什么第一个有效而第二个无效?这只是因为编译器对于作为第二个参数给出的表达式类型具有一定的灵活性
intSet1
是一个独立的表达式,
Set.of(12312341301)
是一个多边形表达式(请参见和) . 在第二种情况下,上下文允许编译器计算一个具体的
T
类型,该类型与第一个参数的类型
String
兼容。

当您(作为人类)看到第二个编译的
是否有效时,可能会想——这怎么可能?编译器将类型
T
推断为
String
Integer
,因此调用必须绝对失败

编译器在查看方法调用时会以一种完全不同的方式进行思考。它查看方法参数和提供的类型,并尝试为您推断一个完全不同且不符合预期的类型。有时,这些类型是“不可表示的”,即编译器可以存在的类型,但作为用户,您不能声明此类类型

有一个特殊的(未记录的)标志,您可以使用它编译类,并了解编译器是如何“思考”的:

产出将很长,但我们关心的主要部分是:

  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
实例化签名:(INT#1,Set)布尔值
目标类型:
其中T是一个类型变量:
在方法isValid(T,Set)中声明的T扩展对象
其中INT#1、INT#2为交叉口类型:
INT#1扩展对象、可序列化、可比较、常量、常量

您可以看到,推断和使用的类型不是
String
Integer

整洁,我不知道“poly expression”的名称。但是,既然这是权威的消息来源(或者可能是两者的链接),那么这个链接是否应该转到?谢谢,@JoachimSauer。也将添加JLS链接。我刚刚想起jsr链接,这是我第一次读到它。这种类型的泛型不是有点奇怪,可能会产生漏洞吗。虽然需要进行测试,但是still@NishantLakhara最糟糕的是,开发人员很难理解它们的复杂性。但是如果我们没有多边形表达式的好处,那么使用泛型编程将非常麻烦(例如,lambda表达式将立即成为一个难题)。不过,当编译器计算出开发人员没有预料到或检测到的类型时,可能会出现意外,但我相信任何经过测试的代码都可以轻松处理这方面的问题。不过,我不能认为它不安全。@Eugene:谢谢,我很清楚它们是什么,它们做什么,我只是不知道这个概念的名称。可能Eclipse以某种方式为您简化了这一点,但类型是。@Eran感谢您的更正,我没有读到这一行的注释。为了方便其他人,我将我的IdeOne.com链接移到了问题中。
Set<Serializable> intSet1 = Set.of(123,1234,101);
isValid(strValue, intSet1);
 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