理解和运用Java中的Lambda (3)

接口类型 接口实例临时变量 = (方法参数类型X 方法参数类型X临时变量 , 方法参数类型Y 方法参数类型Y临时变量...) -> { 方法体... return 接口抽象方法返回值对应类型类型实例;}

理解和运用Java中的Lambda

一个Lambda表达式由五个部分组成:

返回值:接口类型以及接口类型对应的临时实例变量。

等号:=。

方法参数列表:一般由中括号()包裹,格式是(类型1 类型1的临时变量,...,类型N 类型N的临时变量),在方法没有重载可以明确推断参数类型的时候,参数类型可以省略,只留下临时变量列表。特殊地,空参数列表用()表示,如果参数只有一个,可以省略()。

箭头:->。

方法体:一般由花括号{}包裹,格式是{方法逻辑... return 函数式接口方法返回值类型的值;},有几点需要注意:

如果方法体是空实现,用{}表示,如Runnable runnable = () -> {};。

如果函数式接口抽象方法的返回值为void类型,则不需要return关键字语句,如Runnable runnable = () -> {int i=0; i++;};。

如果函数式接口抽象方法的方法体仅仅包含一个表达式,则不需要使用{}包裹,如Runnable runnable = () -> System.out.println("Hello World!");。

举一些例子:

// Function - 具体 java.util.function.Function<String, Integer> functionY = (String string) -> { return Integer.parseInt(string); }; // Function - 简化 java.util.function.Function<String, Integer> functionX = string -> Integer.parseInt(string); // Runnable - 具体 Runnable runnableX = () -> { System.out.println("Hello World!"); }; // Runnable - 简化 Runnable runnableY = () -> System.out.println("Hello World!"); // 整数1-100的和 - 具体 int reduceX = IntStream.range(1, 101).reduce(0, (int addend, int augend) -> { return addend + augend; }); // 整数1-100的和 - 简化 int reduceY = IntStream.range(1, 101).reduce(0, Integer::sum); 目标类型与类型推断

先引入下面的一个场景:

// club.throwable.Runnable @FunctionalInterface public interface Runnable { void run(); static void main(String[] args) throws Exception { java.lang.Runnable langRunnable = () -> {}; club.throwable.Runnable customRunnable = () -> {}; langRunnable.run(); customRunnable.run(); } }

笔者定义了一个和java.lang.Runnable完全一致的函数式接口club.throwable.Runnable,上面main()方法中,可以看到两个接口对应的Lambda表达式的方法体实现也是完全一致,但是很明显最终可以使用不同类型的接口去接收返回值,也就是这两个Lambda的类型是不相同的。而这两个Lambda表达式返回值的类型是我们最终期待的返回值类型(expecting a data type of XX),那么Lambda表达式就是对应的被期待的类型,这个被期待的类型就是Lambda表达式的目标类型

为了确定Lambda表达式的目标类型,Java编译器会基于对应的Lambda表达式,使用上下文或者场景进行综合推导,判断的一个因素就是上下文中对该Lambda表达式所期待的类型。因此,只能在Java编译器能够正确推断Lambda表达式目标类型的场景下才能使用Lambda表达式,这些场景包括:

变量声明。

赋值。

返回语句。

数组初始化器。

Lambda表达式函数体。

条件表达式(condition ? processIfTrue() : processIfFalse())。

类型转换(Cast)表达式。

Lambda表达式除了目标类型,还包含参数列表和方法体,而方法体需要依赖于参数列表进行实现,所以方法参数也是决定目标类型的一个因素。

方法参数的类型推导的过程主要依赖于两个语言特性:重载解析(Overload Resolution)和参数类型推导(Type Argument Inference)。

原文:For method arguments, the Java compiler determines the target type with two other language features: overload resolution and type argument inference

重载解析会为一个给定的方法调用(Method Invocation)寻找最合适的方法声明(Method Declaration)。由于不同的声明具有不同的签名,当Lambda表达式作为方法参数时,重载解析就会影响到Lambda表达式的目标类型。编译器会根据它对该Lambda表达式的所提供的信息的理解做出决定。如果Lambda表达式具有显式类型(参数类型被显式指定),编译器就可以直接使用Lambda表达式的返回类型;如果Lambda表达式具有隐式类型(参数类型被推导而知),重载解析则会忽略Lambda表达式函数体而只依赖Lambda表达式参数的数量。

举个例子:

// 显式类型 Function<String, String> functionX = (String x) -> x; // 隐式类型 Function<String, Integer> functionY = x -> Integer.parseInt(x);

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjwpd.html