尝试在泛型类中应用lambda表达式时输入不匹配编译错误

gb9*_*b96 2 java generics java-8

在下面的Java 8代码示例中,所有方法都是等效的,除了foo3()之外的所有方法都是编译的.

  1. 为什么foo2()编译但foo3()产生编译错误(详见代码注释)?
  2. 为什么javac不允许在foo3中从Object到T的未经检查的强制转换,但是允许在foo4()中从Function <Object,Object>到Function <T,T>的未经检查的强制转换?
  3. 在编译的4种方法中,哪一种最好?

我怀疑这里涉及的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));
  }
}
Run Code Online (Sandbox Code Playgroud)

pru*_*nge 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);
}
Run Code Online (Sandbox Code Playgroud)

泛型类型推断Java教程很好地描述了规则 - "目标类型"部分最相关.

在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);
}
Run Code Online (Sandbox Code Playgroud)

将在Java 8中正常编译,但在早期版本中失败.

但编译器不会从链式方法调用中推断出通用参数,这就是为什么需要像foo5()那样为编译器提供类型参数的显式"提示".