对原始类型的方法引用有害吗?
Method references to raw types harmful?
下面的代码包含对 Enum::name
的引用(注意没有类型参数)。
public static <T extends Enum<T>> ColumnType<T, String> enumColumn(Class<T> klazz) {
return simpleColumn((row, label) -> valueOf(klazz, row.getString(label)), Enum::name);
}
public static <T, R> ColumnType<T, R> simpleColumn(BiFunction<JsonObject, String, T> readFromJson,
Function<T, R> writeToDb) {
// ...
}
Javac编译时报警告:
[WARNING] found raw type: java.lang.Enum missing type arguments for
generic class java.lang.Enum
将表达式更改为 Enum<T>::name
会导致警告消失。
但是 Idea 标记 Enum<T>::name
版本并警告:
Explicit type arguments can be inferred
反过来,Eclipse (ECJ) 没有报告任何公式的任何问题。
三种做法哪个正确?
一方面,原始类型相当讨厌。如果您尝试输入其他类型的参数,例如Enum<Clause>::name
会导致编译失败,所以这是一些额外的保护。
另一方面,上述引用等同于 e -> e.name()
lambda,并且此公式不需要类型参数。
环境:
- Java8u91
- IDEA 15.0.3 社区
- ECJ 4.5.2
没有“原始方法参考”这样的东西。虽然存在原始类型以帮助迁移前泛型代码,但不能有任何方法引用的前泛型使用,因此没有“兼容模式”,类型推断是常态。 Java Language Specification §15.13. Method Reference Expressions 状态:
If a method or constructor is generic, the appropriate type arguments may either be inferred or provided explicitly. Similarly, the type arguments of a generic type mentioned by the method reference expression may be provided explicitly or inferred.
Method reference expressions are always poly expressions
因此,虽然您可以在 ::
之前的类型引用泛型 class 而不指定类型参数时将其称为“原始类型”,但编译器仍会根据其推断泛型类型签名到目标函数类型。这就是为什么在这里生成有关“原始类型使用”的警告没有意义。
请注意,例如
BiFunction<List<String>,Integer,String> f1 = List::get;
Function<Enum<Thread.State>,String> f2 = Enum::name;
可以在没有任何警告的情况下使用 javac
进行编译(规范名称类似于应该推断类型的示例),而
Function<Thread.State,String> f3 = Enum::name;
生成警告。关于这个案例的specification says:
In the second search, if P<sub>1</sub>
, ..., P<sub>n</sub>
is not empty and P<sub>1</sub>
is a subtype of ReferenceType, then the method reference expression is treated as if it were a method invocation expression with argument expressions of types P<sub>2</sub>
, ..., P<sub>n</sub>
. If ReferenceType is a raw type, and there exists a parameterization of this type, G<...>
, that is a supertype of P<sub>1</sub>
, the type to search is the result of capture conversion (§5.1.10) applied to G<...>
;…
所以在上面的例子中,编译器应该推断 Enum<Thread.State>
作为 Enum
的参数化,它是 Thread.State
的超类型,以搜索合适的方法并得出相同的结果f2
示例的结果。它以某种方式 确实 起作用,尽管它会生成无意义的原始类型警告。
显然,javac
仅在必须搜索合适的超类型时才会生成此警告,因此对于您的情况有一个简单的解决方案。只需使用确切的类型进行搜索:
public static <T extends Enum<T>> ColumnType<T, String> enumColumn(Class<T> klazz) {
return simpleColumn((row, label) -> valueOf(klazz, row.getString(label)), T::name);
}
编译时没有任何警告。
下面的代码包含对 Enum::name
的引用(注意没有类型参数)。
public static <T extends Enum<T>> ColumnType<T, String> enumColumn(Class<T> klazz) {
return simpleColumn((row, label) -> valueOf(klazz, row.getString(label)), Enum::name);
}
public static <T, R> ColumnType<T, R> simpleColumn(BiFunction<JsonObject, String, T> readFromJson,
Function<T, R> writeToDb) {
// ...
}
Javac编译时报警告:
[WARNING] found raw type: java.lang.Enum missing type arguments for generic class java.lang.Enum
将表达式更改为 Enum<T>::name
会导致警告消失。
但是 Idea 标记 Enum<T>::name
版本并警告:
Explicit type arguments can be inferred
反过来,Eclipse (ECJ) 没有报告任何公式的任何问题。
三种做法哪个正确?
一方面,原始类型相当讨厌。如果您尝试输入其他类型的参数,例如Enum<Clause>::name
会导致编译失败,所以这是一些额外的保护。
另一方面,上述引用等同于 e -> e.name()
lambda,并且此公式不需要类型参数。
环境:
- Java8u91
- IDEA 15.0.3 社区
- ECJ 4.5.2
没有“原始方法参考”这样的东西。虽然存在原始类型以帮助迁移前泛型代码,但不能有任何方法引用的前泛型使用,因此没有“兼容模式”,类型推断是常态。 Java Language Specification §15.13. Method Reference Expressions 状态:
If a method or constructor is generic, the appropriate type arguments may either be inferred or provided explicitly. Similarly, the type arguments of a generic type mentioned by the method reference expression may be provided explicitly or inferred.
Method reference expressions are always poly expressions
因此,虽然您可以在 ::
之前的类型引用泛型 class 而不指定类型参数时将其称为“原始类型”,但编译器仍会根据其推断泛型类型签名到目标函数类型。这就是为什么在这里生成有关“原始类型使用”的警告没有意义。
请注意,例如
BiFunction<List<String>,Integer,String> f1 = List::get;
Function<Enum<Thread.State>,String> f2 = Enum::name;
可以在没有任何警告的情况下使用 javac
进行编译(规范名称类似于应该推断类型的示例),而
Function<Thread.State,String> f3 = Enum::name;
生成警告。关于这个案例的specification says:
In the second search, if
P<sub>1</sub>
, ...,P<sub>n</sub>
is not empty andP<sub>1</sub>
is a subtype of ReferenceType, then the method reference expression is treated as if it were a method invocation expression with argument expressions of typesP<sub>2</sub>
, ...,P<sub>n</sub>
. If ReferenceType is a raw type, and there exists a parameterization of this type,G<...>
, that is a supertype ofP<sub>1</sub>
, the type to search is the result of capture conversion (§5.1.10) applied toG<...>
;…
所以在上面的例子中,编译器应该推断 Enum<Thread.State>
作为 Enum
的参数化,它是 Thread.State
的超类型,以搜索合适的方法并得出相同的结果f2
示例的结果。它以某种方式 确实 起作用,尽管它会生成无意义的原始类型警告。
显然,javac
仅在必须搜索合适的超类型时才会生成此警告,因此对于您的情况有一个简单的解决方案。只需使用确切的类型进行搜索:
public static <T extends Enum<T>> ColumnType<T, String> enumColumn(Class<T> klazz) {
return simpleColumn((row, label) -> valueOf(klazz, row.getString(label)), T::name);
}
编译时没有任何警告。