摘要
一篇让你搞懂lambda表达式、函数式接口、方法引用、Optional这些JAVA8的新特性及相互间关系。下图是本文的内容概要
函数式接口(Functional Interface)
如果一个接口的定义带有@FunctionanlInterface
注解,它就是函数式接口。但其实函数式接口不一定非要带这个注解,只要满足一个条件:只有一个抽象的方法。
注意:静态方法,默认方法和覆盖了Object的方法都不算抽象方法
我们来定义一个函数式接口:
@FunctionalInterface
public interface Counter {
@Override
boolean equals(Counter c);
public static String toString(Counter c);
public int count();
}
这是计数器接口只有一个计数方法count()是抽象方法
JDK 内置的函数函数式接口方法
1,Consumer<T>
T为输入参数类型,执行某个动作但无返回值,例如
Consumer<String> print = (x)-> {
System.our.println(x);
}
等号前是函数式接口print的定义,等号后就是lambda表达式,这里可以先透漏下lambda表达式的类型就是函数式接口,后面会说明.
2,Supplier<T>
无输入,返回类型是T
3,Predicate<T>
T为输入类型,返回boolean
4,Function<T,R>
T为输入类型,R为返回值类型
5, BinaryOperator<T>
输入参数有两个,类型都是T,返回值类型也是T,常见用于reduce操作
JDK内置函数式接口的其他常见的还有Runable
, Callable
,Comparator
等
lambda表达式(Lambda Express)
() => System.out.println("This is a lambda express");
上面就是个简单的lambda表达式,上文中看到它是可以被赋值给一个变量的。这个变量的类型是函数式接口,而Lambda表达式本身就是函数式接口中定义的那个唯一的抽象方法的实现。
lambda表达式最直观的作用就是使代码非常简洁,有两个场景非常适合使用lambda表达式
1, 函数式接口的实现只需要用到一次
public void testMethod(Printer<String> printer, String content){
printer.print(content);
}
假设testMethod方法作用是使用接口printer打印第二个参数中字符串.
如果是JAVA7首先必须定义好这个接口的实现类,然后在用到接口方法的时候,需要先创建实现类然后才能调用方法。
// java7
// 必须先定义好实现类
public class PrinterImpl implements Printer {
@Override
public void print(String s){
System.our.println(s);
}
}
// 先创建实现类实例,再完成调用
Printer printer = new PrinterImpl();
testMethod(printer, "hello");
在JAVA8 中使用lambda表达式就简洁多了
// java8
testMethod((x)->System.out.println(x), "hello");
而且最重要的是:如果接口实现只需要用一次,你的项目中可以省去一个类。
2, 与forEach, Stream API及方法引用结合使用
假设有如下学生类定义
public class Student {
private String firstName;
private String lastName;
private int age;
// 省略getter和setter方法
}
现有几个学生,需要找出firstName以字母M开头的并打印
List<Student> superStudents = Arrays.asList(
new Student("Huateng","Ma", 4);
new Student("Yun","Ma", 5);
new Student("SK", "Ma", 6);
new Student("Qiangdong", "Liu", 4)
);
//notice
superStudents.stream().filter(s->s.getFirstName.startWith("M")).forEach(System.out::print);
看//notice下面那句是否非常简洁优雅。你可能现在不明白stream是什么,还有forEach里的表达式是怎么回事。没关系,往下看就知道了。
方法引用(Method References)
上个实例代码中, System.out::print
就是方法应用的写法,它可以进一步简化lambda表达式.
可以看到,你只需要使用方法的名字就行,具体的调用会交给函数式接口处理。除了普通方法,构造器方法和静态方法也可以使用方法引用。
1, 构造器方法引用
格式: Class::new,调用默认构造器
2, 静态方法引用
格式: Class::static_method
3,普通方法引用
格式1:Class::method,方法不能带参数
格式2:instance::method
Optional
最后介绍下空指针异常的大杀器Optional.它单独使用时简化代码效果不明显,需要和lambda表达式配合使用。比如下面几种常见写法:
1, 若存在,执行动作
// 假设stuOpt是Optional包装的Student对象
stuOpt.ifPresent(doSomething())
2,若存在,则返回被包装对象,否则返回空
return stuOpt.orElse(null)
3, 若存在,则返回被包装对象,否则执行方法获取
return stuOpt.orElseGet(()->fetchNew())
4, 嵌套null检查
return stuOpt.map(s->s.getSchool()).map(school->school.getPresent()).orElse(null);
总结
lambda表达式可以使代码异常简洁,结合方法引用可以进一步简化代码。lambda表达式可被赋值给函数式接口,它本身就是函数式接口中抽象方法的实现。lambda表达式结合Optional使用可以简化对null的处理。
至于Steam API的介绍,参考Stream API概念及用法详解
来源:CSDN
作者:Lamb_IT
链接:https://blog.csdn.net/Lamb_IT/article/details/100176138