9

下面的代码包含对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 编译时报警告:

[警告] 找到原始类型:java.lang.Enum 缺少泛型类 java.lang.Enum 的类型参数

将表达式更改为Enum<T>::name会导致警告消失。

然而,IdeaEnum<T>::name用以下警告标记版本:

可以推断出显式类型参数

反过来,Eclipse (ECJ) 没有报告任何一种配方的任何问题。

三种方法哪一种是正确的?

一方面,原始类型相当讨厌。如果您尝试放置一些其他类型的参数,例如 Enum<Clause>::name将导致编译失败,那么这是一些额外的保护。

另一方面,上面的引用等同于e -> e.name()lambda,并且这个公式不需要类型参数。

环境:

  • Java 8u91
  • IDEA 15.0.3 社区
  • 欧洲法院 4.5.2
4

1 回答 1

7

没有“原始方法参考”之类的东西。虽然存在原始类型以帮助迁移 pre-Generics 代码,但不能有任何 pre-Generics 使用方法引用,因此没有“兼容模式”并且类型推断是常态。Java 语言规范§15.13方法参考表达式指出:

如果方法或构造函数是泛型的,则可以推断或显式提供适当的类型参数。类似地,方法引用表达式提到的泛型类型的类型参数可以显式提供或推断。

方法引用表达式始终是 poly 表达式

因此,虽然您可以在::引用泛型类而不指定类型参数的情况下调用“原始类型”之前的类型,但编译器仍会根据目标函数类型推断泛型类型签名。这就是为什么在这里产生关于“原始类型使用”的警告是没有意义的。

请注意,例如

BiFunction<List<String>,Integer,String> f1 = List::get;
Function<Enum<Thread.State>,String> f2 = Enum::name;

可以在javac没有任何警告的情况下编译(规范命名了应该推断类型的类似示例),而

Function<Thread.State,String> f3 = Enum::name;

生成警告。规范说明了这种情况:

在第二次搜索中,如果, ...不为空并且是ReferenceType的子类型,则方法引用表达式被视为方法调用表达式,其参数表达式类型为, ..., 。如果ReferenceType是原始类型,并且存在此类型的参数化,即 的超类型,则要搜索的类型是应用于;...</p> 的捕获转换(第 5.1.10 节)的结果P1PnP1P2PnG<...>P1G<...>

所以在上面的例子中,编译器应该推断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);
}

这编译没有任何警告。

于 2016-05-12T14:20:07.933 回答