方法引用原始类型有害吗?

Jak*_*ski 8 javac raw-types java-8 method-reference ecj

下面的代码包含对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) {
 // ...
}
Run Code Online (Sandbox Code Playgroud)

Javac在编译期间报告警告:

[WARNING]发现原始类型:java.lang.Enum缺少泛型类java.lang.Enum的类型参数

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

但是,Idea会在Enum<T>::name版本上标记以下警告:

可以推断出显式类型参数

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

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

一方面原始类型相当令人讨厌.如果你试图放一些其他的类型参数,例如 Enum<Clause>::name会导致编译失败,所以这是一些额外的保护.

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

Enviorment:

  • Java 8u91
  • IDEA 15.0.3社区
  • ECJ 4.5.2

Hol*_*ger 6

没有"原始方法参考"这样的东西.虽然存在原始类型以帮助迁移前泛型代码,但是没有任何前泛型使用方法引用,因此没有"兼容模式"并且类型推断是常态.在Java语言规范§15.13.方法参考表达式指出:

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

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

因此,当您在::没有指定类型参数的情况下引用泛型类时,可以在"原始类型" 之前调用该类型,但编译器仍将根据目标函数类型推断泛型类型签名.这就是为什么产生关于"原始类型使用"的警告在这里没有任何意义.

请注意,例如

BiFunction<List<String>,Integer,String> f1 = List::get;
Function<Enum<Thread.State>,String> f2 = Enum::name;
Run Code Online (Sandbox Code Playgroud)

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

Function<Thread.State,String> f3 = Enum::name;
Run Code Online (Sandbox Code Playgroud)

生成警告.该规范说这个案子:

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

因此,在上面的示例中,编译器应该推断出Enum<Thread.State>该参数化EnumThread.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);
}
Run Code Online (Sandbox Code Playgroud)

这编译没有任何警告.

  • 这将需要*目标类型*的功能签名中的原始类型,但是,当然,应该在*那里*生成适当的警告。例如,考虑“BiConsumer&lt;List,Object&gt; add=List::add;”,它表示未经检查的操作,但负责的是“BiConsumer”的声明,而不是方法引用。 (2认同)