这章节用来讲解Lambda表达式的基本用法和Lambda表达式的语法格式。为接下来深度学习java8新特性做好准备。
一.初步学习Lambda表达式
java8中引用了一个新的操作符“->”,该操作符我们可以称为箭头操作符或者是Lambda操作符。
这个操作符又把Lambda表达式分为两部分:
左侧: 表达式的参数列表(例如:我们用Lambda表达式去实现一个接口,完成接口中的实现方法,而左侧就是接口中的方法的形参列表)
右侧:表达式所需要执行的功能,即Lambda体。
注意:Lambda表达式需要函数式接口的支持。
什么是函数式接口:
1.只有一个方法的接口,我们称之为函数式接口。
2.我们可以通过Lambda表达式去实现这个接口中的方法功能,并创建该接口的对象
3.我们可以在任意函数式接口上使用@FunctionalInterface注解,这样做可以检查它是否是一个函数式接口, 同时javadoc也会包含一条声明,说明这个接口是一个函数式接口。
当函数式接口中如果有多条抽象方法,使用@FunctionalInterface注解的接口就会报错。
二.lambda表达式的基本语法格式
Lambda表达式的语法格式一:
无参数,无返回值。
()->system.out.println(“Hello Lambda!”);
例子:
@Test
public void test() {
//典型例子:Runnable接口,只有一个run方法,无参数无返回值
//原先创建一个Runnable接口实现run方法
Runnable r=new Runnable(){
int num=0; //在JDK1.7中,我们匿名内部类调用局部变量必须加上final关键字
//而在jdk1.8中,我们不再需要加上final,只要我们还未重新赋值的情况下,底层会自动帮我们加上final关键字
@Override
public void run() {
System.out.println("Hello Runable!");
}
};
r.run();
System.out.println("===============================================");
//现在用Lambda表达式实现Runnable接口
Runnable r1= () ->System.out.println("Hello Lambda!");
r.run();
}
还有一点:在jdk1.7中,我们在匿名内部类中的局部变量要加上final关键字。而jdk1.8则不再需要了~
这里就不得不说一下,为什么局部变量在内部类中需要加上final关键字了:
局部变量如果没有用final修饰,他的生命周期和方法的生命周期是一样的,当方法弹栈,这个局部变量也会消失,那么如果局部内部类对象还没有马上消失想用这个局部变量,就没有了,如果用final修饰会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在,也可以继续使用。
简单来说就是:
如果不是final局部变量,当方法执行完后,你的局部变量随着方法弹栈结束,局部变量也随之消失。但是内部类和我的方法并不是同时执行的,如果说没有加上final,这时候局部变量消失了,内部类又想要使用这个变量,那就惨了呀~
jdk1.8局部变量在内部类中不需要加上final关键字的原因:
而对于jdk1.8来说,对于内部类来说,局部变量不用再加上final关键字,不是因为不需要了,而是在java8底层自动帮助加上final关键字,只是对于我们程序员来说更加简便了。
Lambda表达式的语法格式二:
语法格式二:一个参数,无返回值
(x)->system.out.println(“Hello Lambda”);
在java8中有一个consumer接口(字意:消费者),而这个接口有一个方法,只有一个参数,无返回值。
@Test
public void test1(){
Consumer<String> c= (x) -> System.out.println(x);//传入一个字符串
c.accept("Hello Lambda!");//调用接口中的方法
}
Lambda表达式的语法格式三:
语法格式三:有两个以上的参数,并且有返回值,而且执行的功能操作有好几条语句。
(x,y) ->{
System.out.println(“通过Comparator接口比较两个数的大小”);
return Integer.compare(x, y);
};
@Test
public void test2(){
Comparator<Integer> com=(x,y) ->{
System.out.println("通过Comparator接口比较两个数的大小");
return Integer.compare(x, y);//利用Integer底层实现的compare方法进行比较
};
int i= com.compare(1, 2);
System.out.println(i);//-1,说明1<2
}
注意:如果说有两个以上的参数,并且有返回值,但是执行的功能操作只有一条语句,那么return和{}可以省略不写。
@Test
public void test3(){//当Lambda表达式中有多个参数,但是执行的Lambda体只有一条,那么大括号和return可以省略不写
Comparator<Integer> com1=(x,y)->Integer.compare(x, y);
com1.compare(2, 3);
System.out.println("当Lambda表达式中有多个参数,但是执行的Lambda体只有一条,那么大括号和return可以省略不写");
System.out.println();
}
在java8的lambda表达式中,你会发现,lambda表达式的参数类型根本不需要写,这是因为java8底层使用了“类型推断”。
@Test
public void test5(){
Comparator<Integer> com1=(Integer x, Integer y)->Integer.compare(x, y);
int i=com1.compare(2, 3);
System.out.println(i);
//你会发现上面和下面执行的lambda表达式结果一样,都是-1。
//这是因为java8底层使用了“类型推断”,可以根据上下文来推断处参数的类型
Comparator<Integer> com2=(x,y)->Integer.compare(x, y);
int i2=com2.compare(2, 3);
System.out.println(i2);
}
而在java7中,就已经开始使用类型推断了,例如常用的数组,集合:
@Test
public void test6(){
String[] s={"aaa","bbb","ccc"};//底层自动推断处集合中的类型为String字符串
List<String> list=new ArrayList<>();//自动根据上下文推断处ArrayList的集合元素类型
}
自定义函数式接口
函数式接口在上文已经做处解释了,我们来自定义一个函数接口,来做一个运算方法。
1.先自定义一个函数式接口
/**
* 定义一个接口,创建一个抽象方法,然后使用@FunctionalInterface注解,检查它到底是否是一个函数式接口
*/
@FunctionalInterface
public interface AdditionLambda {
public int Addition(int x,int y);
//因为我们给这个接口加上了一个@FunctionalInterface方法,如果这个接口中的抽象方法大于1个,那么就不是函数式接口,那么就会报错
//public int subtraction(int x,int y);
}
2.对函数式接口使用Lambda表达式来完成运算方法
@Test
public void test4(){
AdditionLambda add=(x,y)->x+y;
int i=add.Addition(5, 10);
System.out.println(i);//15
}
3.编译结果: