一、lambda表达式
1. 例子
new Thread(new Runnable(){
public void run(){
sout("执行的线程1");
}
})
代码分析:
- Thread类需要一个Runnable接口作为参数,将任务进行添加到线程中
- 指定run,不得不需要Runnable的实现类
- 而实际上,我们只在乎方法体中的代码
new Thread(()->{
sout("执行的线程2");
});
run方法对应的是午餐无返回值情况
2. 语法的规则
不在乎方法的规则,和返回值,只在乎返回的参数
(参数1,参数2)-> {方法体}
基于函数式编程
->分割参数列表和方法体,参数对应的是方法体中的参数
有参数又返回值的lambda的表达式
List<Student> list = new ArrayList<Student>();
//在新创建一个比较的规则
Collections.sort(list, new Comparator<Student>(){
public int compare(Person o1,Person o2){
return o1.getAge() - o2.getAge();
}
});
for(Person p:list){
sout(p);
}
//使用lambda表达式进行改造 首先需要知道实现那个匿名内部类,重写哪一个方法。对方法进行简化,执行的方法有参数和返回值,则直接进行改写
Collections.sort(list,(person o1,person o2)->{
return o1.getAge() - o2.getAge();
});
注意:这里对应的匿名内部类实际上是一个接口,要使用lambda只能有一个方法。如何保证只能具有一个方法?? 使用@FunctionalInterface注解,被注解修饰的接口只能实现一个方法,提示的作用
3. 语句的省略的规则
在lambda表达式的基础语法上,可以使用省略写法的规则为:
- 小括号内的参数类型可以省略
- 如果小括号内仅有一个参数,则小括号可以省略
- 如果大括号内仅有一个语句,可以同时省略大括号,return关键字及语句分号 (注意:省略的是三部分)
参数只有一个,对应的是接口的方法
public interface StudentService{
String show(String name,Integer age);
}
public interface OrderService{
Integer show(String name);
}
//将接口进行传递,实现他的方法
public static void goStudent(StudentService stiudentService){
stiudentService.show("账单",“22”);
}
public static void goOrder(OrderService orderService){
orderService.show("李四");
}
//在实现的时候,需要新创建一个匿名内部类,进行实现对应的方法
goStudent((String name,Integer age)->
return name+age+"666"; //返回的是对应的方法体
);
//省略的写法
goStudent((String name,Integer age)-> name+age+"666");
————————————————————————————————————————————————————————————————
goOrder((String name)->{
return 666; // 返回的是整形的变量
});
//省略的写法
goOrder(name->666);
______________________________________________
goOrder((String name)->{
sout("hello");
return 666; // 返回的是整形的变量
});
goOrder(name->{
sout("hello");
return 666; // 返回的是整形的变量
});
4、lambda的总结
lambda的使用的条件:
- 方法的参数或局部变量的类型必须是接口的才可以使用
- 接口中只能有一个方法 @FuncationalIntereface
lambda和匿名内部类的对比
- 所需要的类型是不一样的
匿名内部类的类型可以是类、抽象类、接口,Lambda表达式所需的类型必须是接口 2.匿名内部类的方法的个数可以使随意的,但是对于lamda只有一个方法 - 原理:匿名内部类是在编译后形成一个class,Lambda表达式是在程序运行的时候动态的生成class
二、接口中默认的方法和静态的方法
2.1 接口的默认方法
接口的默认方法可以在不影响原来的继承体系的情况下,进行功能的拓展,实现接口的向下兼容。
之前的时候,接口的方法实现类必须全部的实现,java8中可以不用了
将接口的方法定义为defalut
defalut void fly(){
sout("飞");
}
//defalut可以允许实现类不必实现,但可以使用,
在 JDK8 的集合中,就对 Collection 接口进行了拓展,如增加默认方法 stream() 等。既增强了集合的一些功能,而且也能向下兼容,不会对集合现有的继承体系产生影响。
2.2 接口的静态方法(可以进行实现)
public intereface MyStaticIntereface{
static void method(){
sout("hello world");
}
}
//直接通过接口名调用静态方法,不能通过实现类的对象调用
MyStaticIntereface.method();
三、函数式接口
由来:在上面可以发现,我们在使用Lambda表达式的时候,每次都不要关心接口的名字们只关心方法的参数和返回值的类型。为了简化开发,在JDK中提供了大量的函数式的接口
JDK提供了常见的最简单的四种函数式接口:主要在java.util.function包中
- Consumer,消费型接口。接收一个参数,无返回值。其方法有:void accept(T t);
- Supplier,供给型接口。无参数,带返回值。 其方法:T get();
- Function<T, R>,函数型接口。接收一个参数,返回一个结果。其方法:R apply(T t);
- Predicate,断言型接口。接收一个参数,返回boolean值。其方法:boolean test(T t);
四、方法引用
概念:方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法。
我们发现有其他地方(类或者对象)已经存在了相同的逻辑处理方案,那么就可以引用它的方案,而不必重复写逻辑。这就是方法引用。
要求:
方法体只有一行代码。
其次,方法的实现已经存在。
此时,就可以用方法引用替换 lambda 表达式。
五、Optional
Optional类是一个容器类,在之前我们用null表达一个值不存在,现在可以用Optional更好的表达值存在或者不存在,防止出现空指针异常
User user = new User(); //中间过程,user对象或者address对象都有可能为空,从而产生空指针异常
String details = user.getAddress().getDetails();
//多次调用,之前的话是这样进行处理的
private static String getUserAddr(User user){
if(user != null){
Address address = user.getAddress();
if(address != null){
return address.getDetails();
}else {
return "地址信息未填写";
} }else {
return "地址信息未填写";
} }
可以发现,代码冗长,还不利于维护,随着层级关系更深,将会变成灾难(是否依稀记得js的回调地狱)。
六、Stream流
在Collection的接口中定义了两个默认方法,来进行操作集合
default Stream<E> stream() //返回一个顺序流
default Stream<E> parallelStream() //返回一个并行流
Map<Integer,String> map = new HashMap<>();
map.stream().//对应着有关于流的一些操作
可以进行遍历集合,操作集合
主要包括:数据源,中间操作,终止操作
- 中间操作类:似于流水线,每一个中间操作都会返回一个新的流,供下一个操作进行使用
- 终止操作:每一个流只有一个终止的操作,Stream只有遇到终止的操作,他的源才可以执行遍历操作,之后,这个流就不能再使用了
参考:
作者:烟雨星空
链接:juejin.cn/post/686941… 。