lambda表达式演变
如果你想把一块代码
赋给一个Java变量,应该怎么做呢?
比如把下面面右边的那块代码,赋给theCodeBlCock变量
theCodeBlCock = public void doSomething(String s){
System.out.println(s);
}
在Java 8之前,这个是做不到的。但是Java 8问世之后,利用Lambda特性,就可以做到了,下面是简化过程
theCodeBlCock = public void doSomething(String s){
System.out.println(s);
}
public是多余的
theCodeBlCock = void doSomething(String s){
System.out.println(s);
}
doSomething函数名多余,因为已经赋给theCodeBlCock变量
theCodeBlCock = void (String s){
System.out.println(s);
}
编译器可以自己判断返回的类型是啥
theCodeBlCock = (String s){
System.out.println(s);
}
编译器可以自己判断返回的类型是啥
theCodeBlCock = (s){
System.out.println(s);
}
只有一个参数可以不要()
只有一条语句可以不要{}
参数和方法体之间添加“->”符号
theCodeBlCock = s->System.out.println(s);
这样,我们就成功的非常优雅的把“一块代码”赋给了一个变量。而“这块代码”,或者说“这个被赋给一个变量的函数”,就是一个Lambda表达式。
但是这里仍然有一个问题,就是变量aBlockOfCode的类型应该是什么?
在Java 8里面,所有的Lambda的类型都是一个接口,而Lambda表达式本身,也就是”那段代码“,需要是这个接口的实现。这是个人认为理解Lambda的一个关键所在,简而言之就是,Lambda表达式本身就是一个接口的实现。直接这样说可能还是有点让人困扰,我们继续看看例子。我们给上面的aBlockOfCode加上一个类型:
@FunctionalInterface
public interface LambdaInterface{
void doSomething(String s);
}
LambdaInterface theCodeBlCock = s-> System.out.println(s);
这种只有一个接口函数需要被实现的接口类型,我们叫它函数式接口。为了避免后来的人在这个接口中增加接口函数导致其有多个接口函数需要被实现,变成非函数接口,我们可以在这个上面加上一个声明@FunctionalInterface
, 这样别人就无法在里面添加新的接口函数了。
完整的Lambda表达式声明
MyLambdaInterface aBlockOfCode= (s) -> System.out.println(s);
java8之前都是实现接口重写方法
public class LambdaInterfaceImpl implements LambdaInterface{
@Override
public void doSomething(String s) {
System.out.println(s);
}
}
这两种写法本质上是等价的。但是显然,Java 8中的写法更加优雅简洁。并且,由于Lambda可以直接赋值给一个变量,我们就可以直接把Lambda作为参数传给函数, 而传统的Java必须有明确的接口实现的定义,初始化才行
public static void enact(LambdaInterface myLambda,String s){
myLambda.doSomething(s);
}
public static void main(String[] args) {
enact(s -> System.out.println(s) ,"Lambda演变");
}
lambda表达式简介
- Java8引入函数式编程风格
- 可以理解为一种匿名函数的代替
- 通过行为参数传递代码
- Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递),其可以代替实现接口中的抽象方法时的书写匿名内部类的繁琐代码
- 匿名——我们说匿名,是因为它不像普通的方法那样有一个明确的名称:写得少而想得多!
- 函数——我们说它是函数,是因为Lambda函数不像方法那样属于某个特定的类。但和方法一样, Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。
- 传递——Lambda表达式可以作为参数传递给方法或存储在变量中。
- 简洁——无需像匿名类那样写很多模板代码。
lambda表达式构成
java8中引入了一个新的操作符->,该操作符称为箭头操作符或Lambda操作符,该箭头符号将整个Lambda表达式拆分成两部分:
左侧:Lambda表达式的参数列表,即接口中对应抽象方法的参数列表。
右侧:Lambda表达式中所需要执行的功能,即Lambda表达式体。即需要实现的抽象方法的功能体。
Lambda表达式的基本语法
(parameters) -> expression 或 (parameters) -> { statements; }
语法格式
左侧:参数列表
- 无参数形式: ()->Lambda方法体
- 有一个参数: (x)->Lambda方法体;有且只有一个参数,左侧的小括号可以省略不写,即: x->Lambda方法体
- 有两个及以上参数: (x,y,...)->Lambda方法体
- Lambda表达式的参数列表的,数据类型可以省略不写,
因为JVM编译器可以通过上下文推断出数据类型,即'类型推断'
例如: (Integer x,Integer y) -> Integer.compare(x,y);
简化成:(x,y) -> Integer.compare(x,y);
右侧:Lambda表达式体
- 无返回值:参数列表-> { 方法体;}
- 有返回值,Lambda表达式体中有多条语句:参数列表-> { 方法体;return 返回值;}
- 如果lambda体中只有一条语句,那么大括号{}可以省略,return关键字也可以省略::参数列表->方法体
案例:
/**
* 无参数 , 无返回值
*/
@Test
public void test1 (){
Runnable run = new Runnable() {
@Override
public void run() {
System.out.println("old run");
}
};
run.run();
System.out.println("-----------");
Runnable run1 = () -> System.out.println("lambda run");
run1.run();
}
/**
* 有一个参数 , 无返回值
*/
@Test
public void test2(){
List<String> list = new ArrayList<>();
//将consumer接收到的对象进行具体的功能的消费
Consumer<String> consumer = (s) -> list.add(s);
consumer.accept("ddd");
consumer.accept("aaa");
consumer.accept("ccc");
list.forEach(System.out::println);
}
/**
* 有两个或两个以上参数,有返回值,并且 lambda 体中有多条语句
*/
@Test
public void test3(){
Comparator<Integer> com = (x, y) -> {
System.out.println("x为"+x);
System.out.println("y为"+y);
return Integer.compare(x,y);
};
System.out.println(com.compare(1111,111));
}