下面的第一个赋值没有编译,但我不知道为什么,静态方法的方法签名与功能方法签名匹配,尽管它没有使用类型参数。第二行编译得很好,即使除了类型参数化之外它是相同的签名。
这背后的原因是什么?
public class GenericSample<T> {
public static void staticLambdaMtd(Integer a) {
}
public static<X> void staticLambdaMtd2(X a) {
}
// Funca<T> fa1 = GenericSample::staticLambdaMtd;//does not compile !
Funca<T> fa = GenericSample::staticLambdaMtd2;//does compile !
}
interface Funca<A> {
public void funct(A a);
}
回答1
您正在混合泛型参数 T
和类型 Integer
。这是行不通的,因为 T
和 Integer
是不同的类型。
Generics 是不变的。这意味着您只能为 List<Person>
分配具有相同通用参数的另一个列表,即 Person
(不是 Integer
、Cat
或 T
)。同样,我们不能将 Funca<Integer>
分配给 Funca<T>
类型的变量。
有关详细信息,请查看 Oracle 提供的此https://docs.oracle.com/javase/tutorial/java/generics/index.html。
使用静态方法 staticLambdaMtd(Integer a)
创建的函数只能分配给 Funca<Integer>
类型的变量,而不是 Funca<T>
- 即某种任意类型的函数,因为只有 Integer
类型会匹配方法签名,而不是任何类型将在类 GenericSample
实例化时定义的类型(如 String
、Cat
、BigDecimal
)。
第二条语句编译得很好,因为它不需要任何特定类型。 X
和 T
一样只是一个占位符。并且表达式 GenericSample::staticLambdaMtd2
应该被归类为所谓的 https://docs.oracle.com/javase/specs/jls/se17/html/jls-15.html#jls-15.2,即因为您没有提供类型编译器需要从赋值上下文中推断它。
因此,表达式 GenericSample::staticLambdaMtd2
将被推断为类型为 Funca<T>
并且第二条语句将编译得很好。
下面显示的所有分配都是有效的:
// expression on the right could represent only `Funca<Integer>`
Funca<Integer> fa1 = GenericSample::staticLambdaMtd;
// expression on the right can be succefully inferred to
// a function of any type based on the assingment context on the left
Funca<T> fa = GenericSample::staticLambdaMtd2;
Funca<String> fa = GenericSample::staticLambdaMtd2;
Funca<BigDecimal> fa = GenericSample::staticLambdaMtd2;
请注意,表达式 GenericSample::staticLambdaMtd2
可以通过提供泛型参数从 poly 表达式转换为“独立形式”(实际上,规范中提供的定义方法引用始终是 poly 表达式,因此我在引号仅表示将忽略分配上下文)。
而我们如何才能打破它:
// that will not compile for the same reason as the the firt statement doesn't compile
Funca<T> fa3 = GenericSample::<Integer>staticLambdaMtd2;