不论Java8引入λ表达式有什么深层的设计意图和目标,不妨碍我们给Java的λ表达式一个标签:它是匿名类的语法糖。
1.框架与λ表达式
框架中通常定义了一些回调接口,而匿名类、λ表达式是编写回调函数的手段。
还是以最简单的Java框架(对两个double操作后返回一个double值)为例。java8.Lower是一个底层包。
package java8.Lower;
/**
* 最简单的Java框架
*
* @author yqj2065
*/
public interface MyFramework{
public double op(double m,double n);
}各种应用程序(在上层包java8.Upper中)依赖底层包。编写回调的方式可以是单独的子类、匿名类或λ表达式。
package java8.Upper;
public class Imp implements java8.Lower.MyFramework{
@Override public double op(double m,double n){
return m+n;
}
}应用程序App中使用了MyFramework的单独的子类、匿名类或λ表达式
package java8.Upper;
import java8.Lower.MyFramework;
public class App{
public static void main(String[] args){
//片段1
MyFramework f = new Imp();
double d = f.op(1,2);
System.out.println(d);
//片段2
d = new MyFramework(){
@Override public double op(double m,double n){
return m*n;
}
}.op(1,3);
System.out.println(d);
//片段3
f=(double m,double n)->{return 2*m * n ;};
d = f.op(1,4);
System.out.println(d);
}
}
2.Java的lambda表达式不是“匿名方法”
函数式编程语言如Scheme中,依据丘奇的λ演算,它的lambda表达式定义了匿名(没有名字)函数。
((lambda (x) (* x x))2) → 4
当然也可以为匿名(没有名字)函数命名:
( define (square x) (* x x) )
(square 2) → 4 ;;; 函数应用
但是,Java的λ表达式不是没有名字的函数,而是省略了名字的函数。
应用程序App中,片段1,new Imp()创建了有名字即Imp的类的对象;片段2,定义了MyFramework的没有名字的实现类,含有有名字op的方法;片段3中的
f=(double m,double n)->{return 2*m * n ;};
lambda表达式所定义的函数具有名字op,但是被省略。正是因为λ表达式具有被省略的特定名字(换言之,λ表达式肯定使用某个特定名字),所以才能够省略。
现在为例程增加一个函数接口(Functional interfaces)——早期它们被称为SAM(单一抽象方法/Single Abstract Method)类型的接口。
package java8.Upper;
@FunctionalInterface public interface Framework2{
int op(int x);
}下面的两个op正是两个λ表达式各自的名字(分别@Override 了MyFramework和Framework2的op方法)。
f=(double m,double n)->{return 2*m * n ;};
d = f.op(1,4);
System.out.println(d);
Framework2 f2 = x->2*x;
d = f2.op(5);
System.out.println(d);这一省略了名字的函数(Java的λ表达式)依靠或带来了相关概念:
函数接口(Functional interfaces)、目标类型(Target typing)。
不论Java的λ表达式如何向函数式编程语言靠拢,本质上,它是一个(拥有省略了名字的函数的)某个函数接口的匿名类。
3.目标类型(Target typing)
目标类型即作为匿名类的Java的λ表达式的父类型。在下面的片段中,Java的λ表达式 x->2*x到底省略了什么名字呢?或者说, x->2*x到底具有什么目标类型呢?
Framework2 f2 = x->2*x;
d = f2.op(5);
System.out.println(d);
IntUnaryOperator f3 =x->2*x;
d = f3.applyAsInt(5);
System.out.println(d);
在某些环境/上下文中,例如变量声明或赋值中,
Framework2 f2 = x->2*x;
这时的 x->2*x省略的名字为op。IntUnaryOperator f3 =x->2*x;时,省略的名字为applyAsInt。
4.λ表达式的语法
λ表达式有三部分组成:(参数列表),箭头->,函数体。
(参数列表)
既然λ表达式拥有自己的目标类型(是一个函数接口),通常λ表达式不需要写出形参列表中参数的类型;
(double m,double n)->{return 2*m * n ;}或者:( m, n)->{return 2*m * n ;}
一个参数时,可以省略形参列表的括号。如(x)->2*x 或 x->2*x
函数体
λ表达式的函数体可以是表达式,或一个语句块。当函数体为单一的语句而且返回一个结果时,用表达式作为函数体更简洁。如:
Framework2 f2 = x->2*x;
f2 =x->{return 2*x;};
5.高阶函数