目录
写在前面
在之前的面向对象编程中, 代码都是更加倾向于使用对象去操作, 对象做了什么, 对象怎么做,而函数式编程中, 省略了能推导出来的部分的代码, 更倾向于, "做什么"的逻辑.
🌰举个例子
当想创建线程对象, 给定线程任务时, 可以使用匿名内部类去编写:
是创建的Runnable接口实现类对象重写的run方法完成的逻辑
Thread t1 = new Thread(new Runnable{
public void run(){
System.out.println("这个是使用匿名内部类创建的线程任务");
}
});
t1.start();
使用函数式编程Lambda表达式实现相同的逻辑
是Thread的参数接口的实现类对象的抽象方法里面的逻辑
Thread t2 = new Thread(()->System.out.println("这个是使用Lambda表达式写的线程任务"));
t2.start();
同样的逻辑, 使用Lambda表达式让我们的代码更少, 看起来更工整
🍉Lambda表达式的组成
() : 里面没有内容, 表示参数为空
->: 表示指向逻辑
{} : 代码块, 方法体中的内容, 编写逻辑
🍉使用Lambda的前提
调用方法时, 方法的参数是一个接口类型, 接口是函数式接口时, 可以使用Lambda表达式编写抽象方法的实现逻辑作为参数传递
什么是函数式接口(专门为Lambda而生的接口)
这个接口中只能存在一个抽象方法, 但不对其他方法做限制, 为了规范自定义的函数式接口, 可以使用 @FunctionalInterface 去规范限制
🍉Lambda还能省?
Lambda的使用必须具备可推导的环境. 系统能推导出来的内容就可以省略
标准格式是()->{} , 但是满足一定条件的情况下, Lambda表达式还能再省略
1. 当只有一个参数情况下, 小括号()和参数的数据类型可以不写
public interface Flyable {
// 接口中只有一个字符串类型的s 作为参数
String fly(String s);
}
class Test{
public static void main(String[] args) {
// 在调用方法时,可以省略小括号和该参数的数据类型
fly(s->{
s = "鸟儿用翅膀飞翔!";
return s;
});
}
public static void fly(Flyable flyable){
System.out.println(flyable);
}
}
2.当{} 中逻辑只有一句代码时, 可以省略{}, return也可以省略
public interface Flyable {
String fly(String s);
}
class Test{
public static void main(String[] args) {
fly((s)-> s+"用翅膀飞");
}
public static void fly(Flyable flyable){
String s = "鸟儿";
System.out.println("鸟儿怎么飞:"+flyable.fly(s));
}
}
3. 参数的类型可以省略(要么都省略, 要么不省略)
public interface Calculate {
int cal(int a, int b);
}
class Test{
public static void main(String[] args) {
count((a,b)-> a+b);
}
public static void count(Calculate calculate){
int result = calculate.cal(10,50);
System.out.println("计算结果为:"+result);
}
}
🍉常用的函数式接口
函数式接口只有一个抽象方法, 功能有限, 因此正常的业务很少需要自己编写一个函数式接口, 但是java.util.function中提供了很多可以使用的函数式接口工具
1.消费型: Consumer<T>接口
public class Demo {
public static void main(String[] args) {
useConsumer(s -> System.out.println(s));
}
public static void useConsumer(Consumer<String> consumer){
String s = "今天好吗?";
//消费这个字符串
//Consumer的accept方法没有返回值, 使用打印 消费数据
consumer.accept(s);
}
}
foreach方法的参数是Consumer接口
public class Demo {
public static void main(String[] args) {
//使用foreach遍历单列集合
List<String> list = new ArrayList<>();
Collections.addAll(list,"张三","李四","王五","赵六");
list.forEach(s-> System.out.print(s+" "));
System.out.println("=======无情分割线=======");
//使用foreach遍历双列集合
Map<String,Integer> map = new HashMap<>();
map.put("张三",19);
map.put("李四",21);
map.put("王五",13);
map.forEach((k,v)-> System.out.println(k+":"+v));
}
}
2.供给型: Supplier<T> 接口
//当需要提供指定类型的数据时,使用
public class Demo {
public static void main(String[] args) {
useSupplier(()-> new Random().nextInt(100));
}
public static void useSupplier(Supplier<Integer> supplier){
Integer num = supplier.get();
System.out.println("本次得到的数据是:"+num);
}
}
3.谓词型 Predicate<T>接口
//编写自定义的判断逻辑 返回boolean
public class Demo {
public static void main(String[] args) {
usePredicate(s->{
boolean re = s.length()==2;
return re;
});
}
public static void usePredicate(Predicate<String> predicate){
String s = "倪妮";
//test方法返回boolean结果
boolean result = predicate.test(s);
System.out.println("字符串"+s+"是否符合自定义Predicate规则:"+result);
}
}
removeIf方法批量删除单列集合数据,参数是Predicate
public class Demo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
Collections.addAll(list,"张三","李四","王五","张三","赵六","张三");
//removeIf只有单列集合可以使用
//删除索引以张开头的内容
list.removeIf(s->{
boolean re = s.startsWith("张");
return re;
});
//使用foreach遍历
list.forEach(s-> System.out.println(s));
}
}
4.转换型 Function<T, R>接口
//将参数类型数据 转换成另一种类型并返回
public class Demo {
public static void main(String[] args) {
useFunction(s -> Integer.parseInt(s));
}
public static void useFunction(Function<String,Integer> function){
String s = "60";
Integer i = function.apply(s);
System.out.println("转换后的结果和3相加得:"+(i+3));
}
}
🍉Lambda表达式和匿名内部类做参数的区别
1.所需类型:
匿名内部类可以是接口, 也可以是抽象类, 还可以是具体类
Lambda表达式只能是接口
2.使用限制:
如果接口中只有一个抽象方法, 既可以使用匿名内部类也可以使用Lambda表达式
如果接口中多于一个抽象方法, 只能使用匿名内部类
3.编译后的文件
匿名内部类编译后产生单独的.class字节码文件
Lambda表达式编译后, 没有单独的.class字节码文件, 对应的字节码文件会在运行时动态生成