在下面的Java 8代码示例中,所有方法都是等效的,除了foo3()之外都是编译。
我怀疑这里涉及的Java编译时泛型有一些更好的点,但它们对我来说似乎并不清楚,而且远非显而易见。
import java.util.function.Function;
public class LambdaTest<T> {
public T foo1(T t) {
Function<T, T> myIdentity = r -> r;
return myIdentity.apply(t);
}
public T foo2(T t) {
Function<T, T> identity = Function.identity();
return identity.apply(t);
}
public T foo3(T t) {
/* XXX Compile error!
* java.lang.Error: Unresolved compilation problem:
* Type mismatch: cannot convert from Object to T
*/
return Function.identity().apply(t);
}
@SuppressWarnings("unchecked")
public T foo4(T t) {
// unchecked conversion from Function<Object, Object> to Function<T, T>
return ((Function<T, T>) Function.identity()).apply(t);
}
public T foo5(T t) {
// provide an explicit type hint to the compiler
return Function.<T>identity().apply(t);
}
public static void main(String[] args) {
String hello = "Hello world!";
LambdaTest<String> test = new LambdaTest<>();
System.out.println("1. " + test.foo1(hello));
System.out.println("2. " + test.foo2(hello));
System.out.println("3. " + test.foo3(hello));
System.out.println("4. " + test.foo4(hello));
System.out.println("5. " + test.foo5(hello));
}
}
答案 0 :(得分:5)
foo3()
不起作用,因为它没有被赋予类型来计算它。
Collections.emptyList()和没有lambdas的代码会出现类似的问题:
public void bar1()
{
//Works
List<String> list = Collections.emptyList();
String s = list.get(0);
}
public void bar2()
{
//Compile error
String s = Collections.emptyList().get(0);
}
public void bar3()
{
//Works
String s = Collections.<String>emptyList().get(0);
}
Java tutorial for generic type inference很好地描述了规则 - “目标类型”部分最相关。
在bar1()和bar3()中,编译器可以从返回值推断emptyList()
的泛型参数。这适用于Java 5以上。
从Java 8开始,也可以从方法参数推断泛型类型。所以下面的代码:
public void bar4()
{
String s = readFirstElement(Collections.emptyList());
}
private <T> T readFirstElement(List<T> list)
{
return list.get(0);
}
将在Java 8中编译良好,但在早期版本中失败。
但是编译器不会从链式方法调用中推断出通用参数,这就是为什么你需要像foo5()那样为编译器提供类型参数的显式'提示'。
答案 1 :(得分:4)
foo2
有效,因为在Function<T, T> identity = Function.identity();
语句中,identity
方法的类型参数自动选为T
(请参阅javadoc §18.5.1: generic method applicability testing) 。但是在foo3
中,Function.identity()
创建了一个原始Function
对象,而原始apply
对象的Function
方法的返回类型为Object
。 (仅当type参数是赋值表达式之前计算的最后一个表达式的参数时,自动选择type参数才有效)
允许在foo4
中投放,因为只要没有Function
永远不能投放到Function<T,T>
的信息,就允许显式强制转换。 (见java language spec 15.16)。此外,foo3
中也允许实际 的未经检查的广告。以下foo3
方法完全有效:
public T foo3(T t) {
return (T)(Function.identity().apply(t));
}
foo1
,foo2
和foo5
具有相同的“质量”。我选择foo5
,因为它是最短的,并且至少与其他两种方法一样可读。
答案 2 :(得分:0)
Function.identity()
是一个静态泛型方法,它有自己的类型,而T foo3(T t)
需要另一个类型T.所以有必要在某个地方进行投射。 (Function.<T>identity()
,(Function<T, T>) Function.identity())
等等......)