为什么使用Lamdba表达式
我来举一个范例就知道了
使用匿名内部类
public void test1(){
Comparator com = new Comparator() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o2,o1);
}
};
}
上面那么多的代码其实就一句话有用Integer.compare(o2,o1);
使用Lambda表达式
public void test2(){
Comparator com = (o1, o2) -> Integer.compare(o1, o2);
}
是不是很方便了。但可能也会有人觉得并没有什么太大的简便。那么下面再给出一个例子来引入Lambda表达式。
创建Employee类,有name、age、salary三个属性
public class Employee {
private String name;
private int age;
private double salary;
public Employee() {
}
public Employee(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public double getSalary() {
return salary;
}
@Override
public String toString() {
return "name='" + name + '\'' +
", age=" + age +
", salary=" + salary;
}
}
- 需求1:获取员工年龄大于35的员工信息
我们模拟一下数据库,所以准备一个List集合
//Arrays.asList 将数组转换成集合
List employees = Arrays.asList(
new Employee("张三",18,9999.99),
new Employee("李四",38,5555.55),
new Employee("王五",50,6666.66),
new Employee("赵六",16,3333.33),
new Employee("田七",8,7777.77)
);
既然来了需求那我们就要开发呀
/**
* 获取员工年龄大于35的员工信息
* @param list
* @return
*/
public List findEmployeesByAge(Listlist){
Listemps = new ArrayList<>();
for(Employee emp : list){
if(emp.getAge() > 35){
emps.add(emp);
}
}
return emps;
}
并且来波开发自测
@Test
public void test3(){
List employeesByAge = findEmployeesByAge(employees);
for (Employee employee : employeesByAge) {
System.out.println(employee);
}
}
输出结果
name='李四', age=38, salary=5555.55
name='王五', age=50, salary=6666.66
完美!!!美滋滋。完成任务
- 需求2:获取公司中工资大于5000的员工信息
public List findEmployeesBySalary(Listlist){
Listemps = new ArrayList<>();
for(Employee emp : list){
if(emp.getSalary() > 5000){
emps.add(emp);
}
}
return emps;
}
你写完之后发现,特么...就改了个方法名和判断条件,聪明的你发现这代码不能这么写。为什么不能这么写呢!如果你牛逼的客户**,突然又来了一个需求,获取年龄大于20的,薪资大于3000的。你第一个想到的是将判断条件提取为参数,你有推翻了这种想法,你觉得这样不够灵活。突然,你想到了设计模式。
优化一:设计模式之策略模式
创建查询行为的接口
public interface MyPredicate {
public boolean test();
}
并创建相关的实现类代表不同的行为: (分别是年龄> 35和工资> 5000的 )
//获取员工年龄大于35的员工信息
public class FindEmployeesByAge implements MyPredicate{
@Override
public boolean test(Employee emp) {
return emp.getAge() > 35;
}
}
//获取公司中工资大于5000的员工信息
public class FindEmployeesBySalary implements MyPredicate{
@Override
public boolean test(Employee emp) {
return emp.getSalary() > 5000;
}
}
给外部提供一个通用方法
public List filterEmployees(Listlist,MyPredicatemp){
Listemps = new ArrayList<>();
for(Employee emp : list){
if(mp.test(emp)){ //重点代码
emps.add(emp);
}
}
return emps;
}
写了这么久自测一下
@Test
public void test4(){
List employees = filterEmployees(this.employees, new FindEmployeesByAge());
for (Employee employee : employees) {
System.out.println(employee);
}
}
输出结果
name='李四', age=38, salary=5555.55
name='王五', age=50, salary=6666.66
如果想要测试工资大于5000的员工信息,只需要换掉 filterEmployees(this.employees, new FindEmployeesByAge())
中的FindEmployeesByAge为FindEmployeesBySalary
优化二:匿名内部类
好处:不需要创建接口的具体的实现类
(但还是需要MyPredicate接口和filterEmployees()方法):
@Test
public void test5(){
List employees = filterEmployees(this.employees, new MyPredicate() {
@Override
public boolean test(Employee employee) {
return employee.getAge() >= 18;
}
});
for (Employee employee : employees) {
System.out.println(employee);
}
}
输出结果
name='张三', age=18, salary=9999.99
name='李四', age=38, salary=5555.55
name='王五', age=50, salary=6666.66
优化三:Lambda表达式
哈哈哈,优化优化就回到了开头那个例子(*^▽^*)
,Lambda表达式省去匿名内部类的没用的代码,提高了代码的整洁性和可读性(注需要那个filterEmployees方法
)
@Test
public void test6(){
List employees = filterEmployees(this.employees, (e) -> e.getAge() > 15);
for (Employee employee : employees) {
System.out.println(employee);
}
}
输出结果
name='张三', age=18, salary=9999.99
name='李四', age=38, salary=5555.55
name='王五', age=50, salary=6666.66
name='赵六', age=16, salary=3333.33
优化四:使用Stream API
使用该方式,不需要我们上面所创建的所有方法,包括接口、实现类。当然数据还是要的。
@Test
public void test7(){
employees.stream()
.filter((e) -> e.getAge() > 35)
.forEach(System.out::println);
}
输出结果
name='李四', age=38, salary=5555.55
name='王五', age=50, salary=6666.66
Lambda表达式基础语法
Java8中引入了一个新的操作符,"->",该操作符称为箭头操作符或者Lambda操作符,箭头操作符将Lambda表达式拆分成两部分:
- 左侧: Lambda表达式的参数列表,也就是接口中抽象方法的参数列表
- 右侧: Lambda表达式中所需要执行的功能(即:Lambda体),对应的是对抽象方法的实现
注:Lambda表达式需要函数式接口的支持。所谓函数式接口就是接口中只能有一个抽象方法
基础语法
- 语法格式一:无参数,无返回值
@Test
public void test1(){
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("普通 Runnable");
}
};
runnable.run();
System.out.println("=============我是分割线=============");
Runnable runnable1 = () -> System.out.println("Lambda Runnable");System.out.println("Lambda Runnable");
runnable1.run();
}
输出
普通 Runnable
=============我是分割线=============
Lambda Runnable
- 语法格式二:一个参数、无返回值(
参数列表中有且只有一个参数,那么小括号可以省略不写
)
@Test
public void test2(){
Consumer con = (x) -> System.out.println(x);
//Consumer con = x -> System.out.println(x);
con.accept("NiuBi Class");
}
- 语法格式三: 多个参数,有返回值,Lambda体只有一条语句(
大括号和return可以省略
)
@Test
public void test3(){
Comparator com = (x,y) -> Integer.compare(x, y);
}
- 语法格式四:多个参数,有返回值,Lambda体有多条语句(
要用大括号括起来,并且要写上return
)
@Test
public void test4(){
Comparatorcom = (x,y) -> {
System.out.println("函数式接口");
return Integer.compare(x,y);
};
}
- 语法格式五:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出数据类型,这过程称之为"类型推断",例如:
(Integer x,Integer y ) -> Integer.compare(x,y); 可以简写成(x,y) -> Integer.compare(x,y);
函数式接口
- 接口中只有一个抽象方法的接口称为函数式接口
- 使用注解@FunctionlInterface来检查是否是函数式接口
需求: 对一个数进行+-*/的运算:
函数式接口:
@FunctionalInterface
public interface MyFun {
Integer getValue(Integer num);
}
通用方法:
public Integer operation(Integer num,MyFun mf){
return mf.getValue(num);
}
测试:
@Test
public void test5(){
System.out.println(this.operation(100,(x) -> x * x ));
}
四大内置函数式接口
我们发现,如果使用Lambda还要自己写一个接口的话太麻烦,所以Java8提供四大内置函数式接口,只有一小部分比较特殊得情况需要我们自己去定义函数式接口
-
Consumer
消费型接口void accept(T t);
:对类型为T的对象应用操作 -
Supplier
供给型接口T get();
:返回类型为T的对象 -
Function
函数型接口R apply (T t);
:对类型为T的对象应用操作,并返回结果,结果是R类型的对象 -
Predicate
断言形接口boolean test(T t);
:确定类型为T的对象是否满足某约束,并返回boolean值
Consumer
消费型接口 void accept(T t);
@Test
public void test1() {
//例子1
happy(1000,(x)->System.out.println("你在我这消费了" + x + "元"));
//例子2
List list = Arrays.asList("1", "2", "3");
list.forEach((obj) -> System.out.println(obj));
}
Supplier
供给型接口 T get();
需求: 产生指定个数的整数,并放入集合中
@Test
public void test2(){
List numList = getNumList(5, () -> (int) (Math.random() * 100));
numList.forEach((obj)->System.out.println(obj));
}
public List getNumList(int num, Supplier sup){
ArrayList list = new ArrayList<>();
for (int i = 0; i < num; i++) {
Integer value = sup.get();
list.add(value);
}
return list;
}
Function
函数型接口 R apply (T t);
@Test
public void test3(){
//去掉空格
System.out.println(strHandler(" gongj ",(str) -> str.trim()));
//转换大小写
System.out.println(strHandler("gongj", (str) -> str.toUpperCase()));
}
private String strHandler(String str, Function fun){
return fun.apply(str);
}
Predicate 断言形接口 boolean test(T t);
@Test
public void test4(){
List list = Arrays.asList("gongj", "yuanj", "xdd");
System.out.println( filterStr(list,(str) -> str.length() > 3));
}
//判断长度>3的字符串
public List filterStr(List strs, Predicate pre){
ArrayList arrayList = new ArrayList<>();
for (String str : strs) {
if(pre.test(str)){
arrayList.add(str);
}
}
return arrayList;
}
其他类型的函数式接口
https://blog.csdn.net/zxzxzx0119/article/details/82392396
https://blog.csdn.net/rubulai/article/details/88899892