问题
The code below contains a reference to Enum::name
(notice no type parameter).
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 reports a warning during compilation:
[WARNING] found raw type: java.lang.Enum missing type arguments for generic class java.lang.Enum
Changing the expression to Enum<T>::name
causes the warning to go away.
However Idea flags the Enum<T>::name
version with a warning that:
Explicit type arguments can be inferred
In turn Eclipse (ECJ) doesn't report any problems with either formulation.
Which of the three approaches is correct?
On one hand raw types are rather nasty. If you try to put some other type argument e.g. Enum<Clause>::name
will cause the compilation to fails so it's some extra protection.
On the other hand the above reference is equivalent to e -> e.name()
lambda, and this formulation doesn't require type arguments.
Enviorment:
- Java 8u91
- IDEA 15.0.3 Community
- ECJ 4.5.2
回答1:
There is no such thing as a “raw method reference”. Whilst raw types exist to help the migration of pre-Generics code, there can’t be any pre-Generics usage of method references, hence there is no “compatibility mode” and type inference is the norm. The Java Language Specification §15.13. Method Reference Expressions states:
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
So while you may call the type before the ::
a “raw type” when it referes to a generic class without specifying type arguments, the compiler will still infer the generic type signature according to the target function type. That’s why producing a warning about “raw type usage” makes no sense here.
Note that, e.g.
BiFunction<List<String>,Integer,String> f1 = List::get;
Function<Enum<Thread.State>,String> f2 = Enum::name;
can be compiled with javac
without any warning (the specification names similar examples where the type should get inferred), whereas
Function<Thread.State,String> f3 = Enum::name;
generates a warning. The specification says about this case:
In the second search, if
P1
, ...,Pn
is not empty andP1
is a subtype of ReferenceType, then the method reference expression is treated as if it were a method invocation expression with argument expressions of typesP2
, ...,Pn
. If ReferenceType is a raw type, and there exists a parameterization of this type,G<...>
, that is a supertype ofP1
, the type to search is the result of capture conversion (§5.1.10) applied toG<...>
;…
So in the above example, the compiler should infer Enum<Thread.State>
as the parametrization of Enum
that is a supertype of Thread.State
to search for an appropriate method and come to the same result as for the f2
example. It somehow does work, though it generates the nonsensical raw type warning.
Since apparently, javac
only generates this warning when it has to search for an appropriate supertype, there is a simple solution for your case. Just use the exact type to search:
public static <T extends Enum<T>> ColumnType<T, String> enumColumn(Class<T> klazz) {
return simpleColumn((row, label) -> valueOf(klazz, row.getString(label)), T::name);
}
This compiles without any warning.
来源:https://stackoverflow.com/questions/37185734/method-references-to-raw-types-harmful