文章目录
1、函数式编程
相比于面向对象语言,函数式编程强调怎么做,而不是对象。Lambda表达式就是函数式编程思想的体现。
2、Lambda表达式简单例子
Lambda表达式是JDK 8开始后的一种新语法形式,用来简化匿名内部类的代码写法。
简单例子:
// 原来的写法
list.sort(new Compartor<Integer>( {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
// lambda写法
list.sort((Integer o1, Integer o2) -> {
return o1 - o2;
});
// lambda的简化写法,不需要声明参数类型,只有一个语句则无需大括号,只有一个函数返回表达式则无需写return
list.sort((o1, o2) -> o1 - o2;);
可见Lambda表达式只包含参数、箭头操作符、函数体(可能包括返回值)
3、lambda表达式写法
- 不需要声明参数类型,编译器可以统一识别参数值
- 一个参数无需使用圆括号,但无参数或多个参数需要使用圆括号
- lambda体包含一个语句时,无需使用大括号
- 如果主体只有一个函数返回表达式,编译器会自动返回值,无需大括号
1.无参,无返回值
@Test
public void test01(){
Runnable runnable=()-> System.out.println("Runnable 运行");
runnable.run();//结果:Runnable 运行
}
lambda表达式只用于创建只有一个抽象方法的接口,此处是匿名内部类的替代写法;接口可以进行实例化,只需要在new后将接口中未实现的方法进行实现就好,这种做法其实是匿名内部类的做法,本质上讲还是类的实例化
2.一个参数,无返回值
@Test
public void test02(){
Consumer<String> consumer=(x)-> System.out.println(x);
consumer.accept("Hello Consumer");//结果:Hello Consumer
}
括号省略:
public void test02(){
Consumer<String> consumer=x-> System.out.println(x);
consumer.accept("Hello Consumer");//结果:Hello Consumer
}
3.两个参数,Lambda体中多条语句
@Test
public void test04(){
Comparator<Integer> com=(x, y)->{
System.out.println("函数式接口");
return Integer.compare(x,y);
};
System.out.println(com.compare(2,4));//结果:-1
}
4.两个以上参数,有返回值,若Lambda体中只有一条语句,return和大括号都可以省略不写(不可以只省略大括号)
@Test
public void test05(){
Comparator<Integer> com=(x, y)-> Integer.compare(x,y);
System.out.println(com.compare(4,2));//结果:1
}
4、注意事项
(1)接口中只有一个抽象方法时,才能使用lambda表达式
(2)必须有上下文环境,才能推导出lambda表达式对应的接口
什么意思呢?
例如,普通的对于lambda表达式的使用是这样的:
new Thread(() -> System.out.println("线程开启")).start();
也可以使用接口声明来作为上下文:
Runnable r = () -> System.out.println("线程开启");
5、匿名内部类与Lambda表达式的区别
(1)lambda表达式只能用于仅有一个抽象方法的接口,匿名内部类可以用于含有多个抽象方法的接口,实现所有方法即可。
(2)匿名内部类还可以用于抽象类和实现类对应接口的方法匿名实现,lambda表达式只能用于接口。
(3)匿名内部类会产生额外的class文件
6、接口
1)包含的内容:
- public static final 静态常量
- public abstract 抽象方法
- 默认方法(jdk8)
- 静态方法(jdk8)
- 私有方法(jdk9)
2)为什么增加默认方法?
一个接口中本来有两个方法,现在需要增加新的抽象方法,但只需要部分类去实现即可,在jdk8之前的做法是:重新定义一个接口来继承原来的接口,并且其中包含了新增方法。那么需要实现新增方法的类直接实现此子接口即可。原来的实现类无需实现新功能。
但这样做的坏处就是,随着系统开发的功能越来越丰富,接口数量越来越多,需要维护的东西也变得复杂和庞大。
所以增加了默认方法,并且可以在其中实现业务功能,那么别的已实现类也不用额外去实现这个方法了,直接拿来用就可以。当然了,默认方法也可以再次被重写,重写时不用加default关键字
3)静态方法
实现方式和普通类中的一样,在接口中实现静态方法。但使用的时候,只能通过接口名去调用,不可以使用对象名或实现类的名字调用。为什么这样规定?因为java中的类可以实现多个接口,如果这多个接口中包含了名字一样的静态方法,那么通过对象调用此方法时,编译器就不知道到底调用的是哪个接口中的方法。
4)私有方法
当默认方法和静态方法之间有相同的代码时,考虑将它们提取成一个方法,并且隐藏起来,只在默认方法和静态方法中保留对于这段私有代码的调用入口。私有方法可以是普通私有方法,也可以是静态私有方法。但注意,静态方法不能调用非静态方法,所以想要用私有方法代替静态方法内部的重复代码,就将私有方法声明为静态的。
定义格式和在类中一样
7、 函数式接口
即只包含一个抽象方法的接口,我们可以通过Lambda表达式来创建该接口的实现对象。例如Runnable接口
java内置的4大函数式接口:
其他的函数式接口:
8、 方法引用
(1)体验一下
我们知道,lambda表达式为函数式接口提供了方法的实现,如果已经有这个方法的实现,我们就不用再使用lambda表达式实现一遍了,直接将实现好的方法引用过来即可。
// 接口
public interface Printable {
void printString(String s);
}
// 使用Printable的方法printString
public static void main(String[] args) {
// usePrintable((s)-> System.out.println(s)); // 传统的lambda表达式
usePrintable(System.out::println); // System.out对象已经实现有println方法,我们直接引用它,不用管参数,只引用进来它的名字,参数会被自动传给println方法
}
static void usePrintable(Printable p) {
p.printString("test......");
}
(2)正式介绍
::
叫做方法引用符,通过方法的名字来指向一个方法,主要是为了复用已经实现的方法。方法引用是Lambda表达式的另外一种表现形式。无需指定参数类型,也无需指定重载形式,他们都会被自动推导
主要分为四种:
1)引用静态方法
类名::静态方法名
例如:Integer::parseInt
调用接口中抽象方法时,形参全部传递给静态方法
2)引用对象的实例方法
对象名::实例方法名
对象是需要提前创建好的
调用接口中抽象方法时,形参全部传递给实例方法
3)引用类的实例方法
类名::实例方法名
调用接口中抽象方法时,第一个参数是调用该方法的那个对象,后面的参数是引用的方法的参数
4)引用构造器
类名::new
调用接口中抽象方法时,形参全部传递给构造器