可以把lambda表达式理解为简洁的匿名函数,lambda表达式没有名称,但是又参数列表、函数主体、返回类型、还可能抛出一个异常。
先来感受一下lambda的强大//遍历list List再来一个排序的例子list = new ArrayList<>(); Collections.addAll(list,"小黑","小俐","小翔"); list.forEach(item->{ System.out.println(item); }); list.forEach(System.out::println);//简写 //遍历map Map map = new HashMap<>(); map.put("小翔",210); map.put("小俐",160); map.put("小黑",720); map.forEach((k,v) -> System.out.println( k + " : " + v));
List显而易见,使用Lambda表达式以后,代码看起来更清晰更简洁了,以排序的这个例子我们来看一下lambda的组成list = new ArrayList<>(); Collections.addAll(list,3,2,1); //自定义排序规则 list.sort(new Comparator () { @Override public int compare(Integer o1, Integer o2) { return o1 - o2; } }); //lambda写法 list.sort((Integer o1,Integer o2)-> { return o1-o2; }); //输出结果 1 2 3 list.forEach(System.out::println);
参数列表: Integer o1,Integer o2
箭头: ->把参数列表和主体分隔为两个部分
主体: o1-o2
Lambda表达式的基本语法- (参数列表) -> 表达式
- (参数列表) -> { 多条语句 }
当主体为一条语句时{}可以省略 ,当主体是一个表达式时return可以省略。
list.sort((Integer o1,Integer o2)-> o1-o2;);
编译器可以通过函数式接口推断出Lambda表达式的参数类型,所以在编写Lambda表达式时,可以省略参数类型。比如:
list.sort((o1, o2)-> o1-o2;);
当参数列表有且只有一个参数时,参数的小括号可以省略
list.forEach(item->System.out.println(item););
无参的时候()不可用省略,以实现Runnable接口为例
Runnable runnable=()-> System.out.println("Runnable 运行");
有小伙伴会发现我们实现Runnable接口的run()方法并没有指定方法名,那lambda是怎么知道实现那个方法呢?别着急接下来我们来看函数式接口。
函数式接口只包含一个抽象方法的接口,就称为函数式接口。我们可以通过Lambda表达式来创建该接口的实现对象。
我们可以在任意函数式接口上使用@FunctionalInterface注解,这样做可以用于检测它是否是一个函数式接口。
我们来看Runnable接口源码。
@FunctionalInterface public interface Runnable { public abstract void run(); }
自定义函数式接口
@FunctionalInterface public interface MyFuncInterf常用的函数式接口{ public T getValue(String origin); } public class Test { public static void main(String[] args) { Integer length = length(new MyFuncInterf () { @Override public Integer getValue(String origin) { return origin.length(); } },"ABC"); System.out.println(length); //3 //lambda Integer length2 = length(origin->origin.length(),"ABC"); System.out.println(length2);//3 } public static Integer length(MyFuncInterf interf,String str){ Integer length = interf.getValue(str); return length; } }
在Java8支持Lambda表达式以后,为了满足Lambda表达式的一些典型使用场景,JDK为我们提供了大量常用的函数式接口。它们主要在 java.util.function 包中,下面简单介绍几个其中的接口及其使用示例。
Supplier:供给型接口
看一下源码
package java.util.function; @FunctionalInterface public interface Consumer{ void accept(T t); }
public class Student { private String name; private Integer age; public Student() {} public Student(String name, Integer age) { this.name = name; this.age = age; } public Student(Integer age) { this.age = age; } }
下面我们使用Lambda表达式声明一个Supplier的实例:
//实现supplier的get方法并创建supplier的实例: Suppliersupplier = () -> new Student("小黑", 18) Student student = supplier.get();//调用supplier的get方法 System.out.println("name: " + student.getName() + ", age: " + student.getAge());
Consumer:消费型接口
源码如下
package java.util.function; @FunctionalInterface public interface Consumer{ void accept(T t); }
public class Test { public static void main(String[] args) { println(100,num -> System.out.println(num)); } public static void println(Integer money, Consumerconsumer){ consumer.accept(money); } }
Predicate:断言型接口
public static void main(String[] args) { Listlist = new ArrayList<>(); Collections.addAll(list,1,2,3,4,5); List list1 = filterNum(list, (x) -> x < 3); list1.forEach(x-> System.out.println(x)); } public static List filterNum(List list, Predicate predicate){ ArrayList resultList = new ArrayList(); for (Integer item : list) { if (predicate.test(item)){ resultList.add(item); } } return resultList; }
Function: 函数式接口
public static void main(String[] args) { System.out.println(changeNum(1,(x) -> x+1)); } public static Integer changeNum(Integer num, Function方法引用 什么是方法引用?fun){ return fun.apply(num); }
方法引用是Java8中引入的新特性,它提供了一种引用方法而不执行方法的方式,当要传递给Lambda体的操作,已经有实现的方法了,就可以使用方法引用!(实现抽象方法的参数列表,必须与方法引用的参数列表一致,方法的返回值也必须一致,即方法的签名一致)。方法引用可以理解为方法引用是Lambda表达式的另外一种表现形式。
方法引用的语法:使用操作符 :: 将对象或类和方法名分隔开。
eg:
list.forEach(System.out::println);
对象:实例方法名
PrintStream out = System.out; Consumerconsumer = out::println; consumer.accept("hello");
类::静态方法名
静态方法引用使用 类名::方法名
Comparatorcomparable=(x,y)->Integer.compare(x,y); //使用方法引用实现相同效果 Comparator integerComparable = Integer::compare; System.out.println(integerComparable.compare(4,2));//结果:1 System.out.println(comparable.compare(4,2));//结果:1
类::实例方法名
实例方法引用使用 实例名::方法名
BiPredicate构造器引用bp=(x,y)->x.equals(y); //使用方法引用实现相同效果 String str = new String(); BiPredicate bp2 = string::equals; System.out.println(bp.test("1","2"));//结果:false System.out.println(bp.test("1","2"));//结果:false
格式:类名::new
自动与函数式接口中方法兼容,可以把构造器引用赋值给定义的方法。需要注意构造器参数列表要与接口中抽象方法的参数列表一致。使用示例:
public class Student { private String name; private Integer age; public Student() {} public Student(String name, Integer age) { this.name = name; this.age = age; } public Student(Integer age) { this.age = age; } }
//引用无参构造器 Supplier数组引用supplier= Student::new; System.out.println(supplier.get()); //引用有参构造器 Function function = Student::new; System.out.println(function.apply(21)); //引用全参构造器 BiFunction biFunction = Student::new; System.out.println(biFunction.apply("小黑",21));
Functionfunction = String[]::new; String[] apply = function.apply(10); System.out.println(apply.length);//结果:10