首先说明下,jdk8的新特性forEach,Stream在遍历集合时,代码看上去简洁些,但效率会变低
好了,今天开始介绍新特性,先说lambda表达式
Python里有个匿名函数,用lambda定义,jdk8的lambda表达式就相当于一个匿名函数,只是功能强大一些
先说一个概念:
函数式编程:
函数式编程是种编程范式。是相对于命令式编程(常见于面向过程,如C语言),命令式编程是编写,理解和调试代码最容易的方法
举例说明:获取BufferedReader对象
命令式编程:
InputStream in=new InputStream("d:\\test.txt");
InputStreamReader isr=new InputStreamReader(in);
BufferedReader br=new BufferedReader(isr);
函数事编程:调用函数替代命令
BufferedReader br = new BufferedReader(new InputStreamReader(in))
函数式接口(Functional Interface):
jdk8中新增了函数式接口,
函数式接口就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口(修饰符为defalut)。
以前认识的接口中所有方法必须都是抽象的,是不能有非抽象方法的,jdk更新过程中可能对某些接口进行扩展,但是如果在原有接口中加入抽象方法,那实现了该接口的类要全部重新写,为了解决更新接口但兼容以前版本的程序,就可以在函数式接口中可以增加defalut方法
Lambda 表达式和方法引用(实际上也可认为是Lambda表达式)就是针对函数式接口使用的.因为lambda表达式没有方法名,所以针对的就是接口中的抽象方法,说明函数值接口中只能有一个抽象方法,不然天知道lambda重写的是哪个抽象方法
以前如果参数是接口类型的实例,要么自定义类实现接口,要么用匿名内部类,那lambda表达式的引入就可以让匿名内部类的编写变得简洁一些.
函数式接口,如Callable接口(java.util.concurrent.Callable)。
jdk8中新增了@FunctionalInterface注解来显示标注一个函数式接口,并不是所有函数式接口都被标注出来了。该注解主要用于编译级错误检查,加上该注解后,如果自定义接口不符合函数式接口的定义,会报错。当然有没有该注解不会影响是不是函数式接口的本质.
注解样式如下:
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
defalut方法
jdk8中允许接口中定义非抽象方法,在接口中的非抽象方法上使用default修饰即可,加入defalut方法是对接口的补充,好处就是提高程序的兼容性.接口中定义defalut方法变相让java支持多继承,因为可实现多个接口
实际上函数式接口可支持静态方法,继承自java.lang.Object类要重写的public方法,
lambda表达式
上面已经说过,lambda表达式只能用在重写函数式接口的抽象方法上,因为函数式接口只有一个抽象方法,编译器会认为lambda表达式写的方法就是接口的唯一的抽象方法
先看lambda的格式:
(类型 参数, 类型 参数, …, 类型 参数) -> { 代码块; return 结果; }
注:参数类型可省略,编译器可以从上下文环境推断出lambda表达式的参数类型,故可以省略
如箭头右边只有一条语句,则右边的一对大括号可省略
当lambda表达式只有一个参数时,可以省略左边的小括号
对有返回值的抽象方法,如果重写只有一行语句,可以省略掉return关键字,(同时必须省略一对大括号)
格式形如为
( )->{ }
括号 向右的箭头 大括号(方法体)
示例:
自定义函数式接口
@FunctionalInterface
public interface MyInter {
int add(int a, int b);
// defalut修饰的方法
default void sub(int a, int b) {
System.out.println(a - b);
}
// 可写静态方法
static String addString(String s1, String s2) {
return s1 + s2;
}
// 重写继承自Object类的方法,必须是抽象方法,再被实现该接口的类重写
// 这种继承自Object类的方法,不被接口认定为抽象方法(实际是抽象方法)
@Override
boolean equals(Object obj);
}
测试类:
public class Test01 {
public static void main(String[] args) {
//传统匿名内部类的写法
MyInter mi = new MyInter() {
@Override
public int add(int a, int b) {
int temp = a + b;
return temp;
}
};
System.out.println(mi.add(1, 2));
//试用lambda表达式写匿名内部类
MyInter mi2 = (a, b) -> {
return a + b;
};
System.out.println(mi2.add(1, 2));
// 省略return语句
MyInter mi3 = (a, b) -> a + b;
System.out.println(mi3.add(1, 2));
}
}
out:
3
3
3
lambda表达式写多线程示例
public class Test02 {
public static void main(String[] args) {
// 以前写法
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i != 10; i++) {
System.out.println(Thread.currentThread().getName() + " : " + i);// 打印0-9,包括9
}
}
}, "t1");
t1.start();
// 用lambda表达式下多线程
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " : " + i);
}
}, "t2");
t2.start();
}
}
out:
t1 : 0
t1 : 1
t1 : 2
t1 : 3
t1 : 4
t1 : 5
t1 : 6
t1 : 7
t1 : 8
t1 : 9
t2 : 0
t2 : 1
t2 : 2
t2 : 3
t2 : 4
t2 : 5
t2 : 6
t2 : 7
t2 : 8
t2 : 9