上下文推导情形:
1.变量声明
2.赋值
Comparator<String> c;
c = (String s1, String s2) -> s1.compareToIgnoreCase(s2);
3.返回语句
public Runnable toDoLater() {
return () -> {
System.out.println("later");
}
}
4.数组初始化器
filterFiles(new FileFilter[] {
f -> f.exists(), f -> f.canRead(), f -> f.getName().startsWith("q")
});
5.方法和构造方法的参数
List<Person> ps = ...
Stream<String> names = ps.stream().map(p -> p.getName());
在上面的代码中,ps的类型是List<Person>,所以ps.stream()的返回类型是Stream<Person>。map()方法接收一个类型为Function<T, R>的函数式接口,这里T的类型即是Stream元素的类型,也就是Person,而R的类型未知。由于在重载解析之后lambda表达式的目标类型仍然未知,我们就需要推导R的类型:通过对lambda表达式函数体进行类型检查,我们发现函数体返回String,因此R的类型是String,因而map()返回Stream<String>。绝大多数情况下编译器都能解析出正确的类型,但如果碰到无法解析的情况,我们则需要:
使用显式lambda表达式(为参数p提供显式类型)以提供额外的类型信息
把lambda表达式转型为Function<Person, String>
为泛型参数R提供一个实际类型。(.<String>map(p -> p.getName()))
6.lambda表达式函数体(意思就是lambda表达式作为函数体)
lambda表达式本身也可以为它自己的函数体提供目标类型,也就是说lambda表达式可以通过外部目标类型推导出其内部的返回类型,这意味着我们可以方便的编写一个返回函数的函数:
Supplier<Runnable> c = () -> () -> { System.out.println("hi"); };
第一个()等同于Supplier中T get();的get(),后面() -> { System.out.println("hi"); }返回Runnable类型,且这第二个()就是Runnable中的run(),没有返回值。
7.条件表达式(? :)
Callable<Integer> c = flag ? (() -> 23) : (() -> 42);
8.转型(Cast)表达式
// Object o = () -> { System.out.println("hi"); }; 这段代码是非法的
Object o = (Runnable) () -> { System.out.println("hi"); };
目标类型这个概念不仅仅适用于lambda表达式,泛型方法调用和“菱形”构造方法调用也可以从目标类型中受益,下面的代码在Java SE 7是非法的,但在Java SE 8中是合法的:
List<String> ls = Collections.checkedList(new ArrayList<>(), String.class);
函数参数
Set<Integer> si = flag ? Collections.singleton(23) : Collections.emptySet();
条件表达式
此处个人见解:目标类型在之前泛型中常见于变量声明和赋值中,这些应该在8之前已经支持,只是对于方法参数和条件表达式是在8中扩展的。
网友评论