灵魂三问:Lambda表达式是什么?用来做什么?怎么使用?
首先来谈论Lambda表达式是用来做什么的?对于Lambda表达式是什么,怎么使用后面一起总结学习
问题一:Lambda表达式时用来做什么的,也就是Lambda表达式的作用? Lambda表达式是用来简化函数式接口的实现,从而简化我们的开发。有且仅有一个抽象方法的接口被称之为函数式接口,函数式接口可以使用@FunctionalInterface注解修饰。
对比我们已知的实现接口的方式:1、使用implements关键字来实现接口。2、使用内部类的方式实现接口
首先定义一个只有一个方法的接口:
@FunctionalInterface public interface People { public void sayHello(); }1)、使用implements方式来实现接口,代码示例如下
public class Student implements People { @Override public void sayHello() { System.out.println("hello java8新特性"); } } @Test public void test(){ Student stu=new Student(); stu.sayHello(); }2)、使用内部类的方式来实现接口,代码示例如下:
@Test public void test1(){ People people=new People() { @Override public void sayHello() { System.out.println("hello java8新特性"); } } ; people.sayHello(); }3)、使用Lambda表达式来实现接口,代码示例如下:
@Test public void test2(){ People people=()-> System.out.println("hello java8新特性 -- Lambda表达式"); people.sayHello(); }对比发现Lambda表达式对于只有一个抽象方法的接口实现,它大大的简化了我们开发。如果它的作用仅限于此的话,个人觉得它的作用并不明显或者说Lamda表达式没有多大的作用,而它真正的强大之处,个人觉得是配合java8的另一个新特性Stream API进行使用,简化对集合和数组的操作。Stream API这个新特性在之后的博客中会进行介绍
问题二:Lambda表达式是什么?其使用格式?
Lambda表达式的语法格式:()->{}
Java8中引入了一个新的操作符“->”该操作符称之为箭头操作符或者Lambda操作符,将Lambda表达式拆分成两部分。
左侧:Lambda表达式参数列表
右侧:Lambda表达式中所执行的功能,即Lambda表达式
格式一:无参无返回值
()-> System.out.println("无参无返回值")
代码示例:
@Test public void test4(){ Runnable runnable=()-> System.out.println("无参无返回值"); runnable.run(); }格式二:有一个参数,无返回值。
(x)-> System.out.println(x)
代码示例:
@Test public void test5(){ Consumer<String> consumer=(x)-> System.out.println(x); consumer.accept("无参无返回值格式"); }格式三:若只有一个参数,左侧小括号可以不写
x-> System.out.println(x)
代码示例:
@Test public void test6(){ Consumer<String> consumer=x-> System.out.println(x); consumer.accept("只有一个参数,左侧的小括号可以省略"); }格式四:有两个以上参数,且Lambda体中有多条语句
Comparator<Integer> comparator=(x,y)->{
System.out.println("有两个以上参数,且有多条语句的格式");
return Integer.compare(x,y);
};
代码示例:
@Test public void test7(){ Comparator<Integer> comparator=(x,y)->{ System.out.println("有两个以上参数,且有多条语句的格式"); return Integer.compare(x,y); }; }格式五:若Lambda体中只有一条语句,return 和大括号多可以省略
BiFunction<Integer,Integer,Integer> bf=(x,y)->x+y;
@Test public void test8(){ BiFunction<Integer,Integer,Integer> bf=(x,y)->x+y; System.out.println(bf.apply(3,4)); }格式六:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出数据类型,即“类型推断”
BiFunction<Integer,Integer,Integer> bf=(Integer x,Integer y)->x+y;
@Test public void test9(){ BiFunction<Integer,Integer,Integer> bf=(Integer x,Integer y)->x+y; System.out.println(bf.apply(3,4)); }上联:左右遇一括号省
下联:左侧推断类型省
横批:能省则省
Lambda表达式基于函数接口,接下来介绍一下java8提供的四大内置函数式接口和其他扩展型函数式接口
四大内置函数式接口:
一)、Consumer<T> : 消费型接口 方法有一个参数T,无返回值
void accept(T t);
简单示例:
@Test public void test1(){ shopping(10000,(x)-> System.out.println("wife喜欢淘宝,每次消费"+x+"元")); } public void shopping(double money,Consumer<Double> consumer){ consumer.accept(money); }结果:
二)、Supplier<T>:供给型接口 方法无参,有一个返回值T
void get();
简单示例:
@Test public void test2(){ List<Integer> numList = getNumList(10, () -> (int) (Math.random() * 100)); System.out.println(Arrays.asList(numList)); } //随机产生指定个数的整数,放入集合 public List<Integer> getNumList(int num, Supplier<Integer> sup){ List<Integer> list=new ArrayList<>(); for(int i=0;i<num;i++){ Integer number = sup.get(); //get()方法的实现需要随机生成一个整数 list.add(number); } return list; }运行结果:
三)、Function<T, R>:函数型接口 方法有一个参数T,有一个返回值R
R apply(T);
简单示例:
@Test public void test3(){ String s = strHandle("hello ,javaWord", (x) -> x.toUpperCase()); System.out.println(s); String s1 = strHandle("hello ,javaWord", (x) -> x.substring(0, 5)); System.out.println(s1); } //字符串处理 public String strHandle(String str, Function<String,String> fun){ return fun.apply(str); }运行结果:
四)、Predicate<T>:断言型接口 方法有一个参数T,返回值为boolean值
boolean test(T t);
简单示例:
@Test public void Test4(){ String[] strs={"hello","word","ok","now","java"}; List<String> list = filterStr(strs, (x) -> x.length() > 3); //长度大于3的数组放入集合 System.out.println(Arrays.asList(list)); List<String> list1 = filterStr(strs, (x) -> x.contains("o")); System.out.println(Arrays.asList(list1)); } //需求:所有满足条件的字符串放入集合 public List<String> filterStr(String[] strs, Predicate<String> pd){ List<String> list=new ArrayList<>(); for(int i=0;i<strs.length;i++){ if(pd.test(strs[i])){ list.add(strs[i]); } } return list; }运行结果:
其他的一些扩展接口:
所有的函数式接口都存放在rt.jar 中的java.util.function包中
接下来介绍能对Lambda表达式进行简化操作的相关知识点:1)、方法以引用 2)、构造器引用 3)、数组引用
方法引用:若lambda体中的功能,已经有方法提供了实现。那么可以使用方法引用进行替换。
方法引的三种形式:
对象: :实例方法名类: :静态方法名类: :实例方法名1)、简单示例
@Test public void test1(){ Employee emp=new Employee(100,"刘邦",55,11111.11); Supplier<String> sup1=()->emp.getName(); //Lambda表达式 System.out.println(sup1.get()); System.out.println("-------使用方法引用进行替换----------"); Supplier<String> sup2=emp::getName; //对象::实例方法名 System.out.println(sup2.get()); }运行结果
2)、简单示例
@Test public void test2(){ Comparator<Integer> com1=(x,y)->Integer.compare(x,y); //Lambda表达式 System.out.println(com1.compare(3,4)); System.out.println("-------使用方法引用进行替换----------"); Comparator<Integer> com2=Integer::compare; //方法引用: "类名::静态方法名" System.out.println(com2.compare(4,3)); }运行结果:
3)、简单示例:
@Test public void test3(){ BiPredicate<Integer,Integer> bp1=(x,y)->x.equals(y); //Lambda表达式 System.out.println(bp1.test(3,5)); System.out.println("-------使用方法引用进行替换----------"); BiPredicate<Integer,Integer> bp2=Integer::equals; //方法引用: "类名::实例方法名" System.out.println(bp2.test(4,4)); }运行结果
注意:
方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致如果Lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数(或无参)是实列方法的参数时,格式:className: :methodName构造器引用:构造器中的参数列表需与函数式接口中的参数列表保持一致,Lambda表达式可以使用构造器引用进行替换
格式: 类名::new
简单示例:
@Test public void test4(){ Function<String,Employee> fu1=(x)->new Employee(x); System.out.println(fu1.apply("项羽").getName()); System.out.println("---------使用构造器引用进行替换-------------"); Function<String,Employee> fu2=Employee::new; //构造器引用 "类名::new" System.out.println(fu2.apply("范增").getName()); }运行结果:
数组引用: 类型[] :: new;
简单示例:
@Test public void test5(){ Function<Integer,String[]> fu1=(x)->new String[10]; System.out.println(fu1.apply(10).length); System.out.println("---------使用数组引用进行替换-------------"); Function<Integer,String[]> fu2=String[]::new; //构造器引用 "type[]::new" System.out.println(fu2.apply(10).length); }运行结果:
实战练习:
1、使用Collections.sort()方法,通过定制排序比较两个Employee(先按照年龄比,年龄相同,按照姓名比),使用Lambda表达式传递参数 private List<Employee> emps= Arrays.asList( new Employee(103,"萧何",44,8888.77), new Employee(101,"刘邦",55,10000.11), new Employee(102,"张良",45,9999.99), new Employee(106,"陈平",33,5555.55), new Employee(104,"韩信",33,3333.33), new Employee(105,"周勃",29,6666.66) ); //使用Collections.sort()方法,通过定制排序比较两个Employee(先按照年龄比,年龄相同,按照姓名比) @Test public void test1(){ Collections.sort(emps,(e1,e2)->{ if(e1.getAge()==e2.getAge()){ return e1.getName().compareTo(e2.getName()); }else{ return Integer.compare(e1.getAge(),e2.getAge()); } }); for (Employee emp:emps){ System.out.println(emp); } }运行结果:
2、编写一个方法,能对两个整数进行加、减、乘、除运算,使用Lambda表达式实现
@Test public void test2(){ System.out.println("加法运算:"+op(10,2,(x,y)->x+y)); System.out.println("减法运算:"+op(10,2,(x,y)->x-y)); System.out.println("乘法运算:"+op(10,2,(x,y)->x*y)); System.out.println("除法运算:"+op(10,2,(x,y)->x/y)); } //需求:编写一个方法,能对两个整数进行四则运算,使用Lambda表达式实现 public Integer op(Integer num1, Integer num2, BiFunction<Integer,Integer,Integer> bf){ return bf.apply(num1,num2); }运行结果:
总结:对Lambda表达式及相关的知识点进行了整理和学习。
本章的知识点:1)、Lambda表达式的作用和使用、
2)、函数式接口以及java8内置的四大核心函数式接口:消费型接口、供给型接口、函数型接口、断言型接口。
3)、方法引用、构造器引用、数组引用。用于对Lambda表达式的简化操作
参考:张长志老师的视频
学无止境,不进则退