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

时间:2016-05-12 11:36:40

标签: java-8 javac method-reference raw-types 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) {
 // ...
}

Javac在编译期间报告警告:

  

[警告]发现原始类型: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

1 个答案:

答案 0 :(得分:6)

没有“原始方法参考”这样的东西。虽然存在原始类型以帮助迁移前泛型代码,但是没有任何前泛型使用方法引用,因此没有“兼容模式”并且类型推断是常态。 Java Language Specification §15.13. Method Reference Expressions州:

  

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

     

方法引用表达式总是多表达式

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

请注意,例如

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关于此案例:

  

在第二次搜索中,如果P1,...,Pn不为空且P1 ReferenceType 的子类型,则方法参考表达式被视为具有类型P2,...,Pn的参数表达式的方法调用表达式。如果 ReferenceType 是原始类型,并且存在此类型的参数化G<...>,即P1的超类型,则要搜索的类型是捕获转换的结果(§5.1.10)适用于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);
}

此编译没有任何警告。