一、Lambda表达式
1.1、认识与使用Lambda表达式
JDK8推出的一个匿名函数,使用Lambda表达式可以让代码变得更加简洁、灵活。
首先来看一个例子:普通方法重写、Lambda表达式、方法引用
@Test public void test() { //重写匿名接口类 Comparator<Integer> com = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1,o2); } }; //Lambda表达式来表示匿名接口类 Runnable r2 = ()-> System.out.println("changluliner"); //方法引用 Comparator<Integer> com2 = Integer::compareTo; }
对于匿名接口类有三种方式表示。
从下面对比开始使用Lambda表达式
Comparator<Integer> com = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1,o2); } }; //使用lambda表达式 Comparator<Integer> com1 =(o1,o2) -> Integer.compare(o1,o2);
无参数,无返回值:()->{System.out.println(132)}。
有参数,无返回值:(String o1) -> {System.out.println(132)} 或 省略掉()
若Lambda体只有一条语句,可以省略掉{},若是有返回值的也可以省略掉return。
类型推断示例:
@FunctionalInterface public interface Comparator<T> { int compare(T o1, T o2); }
对于该Comparator接口写lambda表达式时()中可以不指定类型,编译器会自动根据你初始变量名中设置的泛型参数决定类型。例如:Compartor<Integer> com = (o1,o2) -> Integer.compare(o1,o2);这里o1、o2默认为Integer类型。
1.2、函数式接口
介绍函数式接口
函数式接口:只包含一个抽象方法的接口,可以通过Lambda表达式来创建该接口的对象。
可使用@FunctionalInterface注解来检查该接口是否为一个函数式接口,同时javadoc也会包含一条声明说明这个接口是一个函数式接口。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface FunctionalInterface {}
当在有多个抽象方法的接口上定义@FunctionalInterface就会出现编译时异常。
说明:以前用匿名实现类表示的都可以使用Lambda表达式来写,举例如下:
@Test public void test() { //Runnable接口 Runnable runnable = () -> System.out.println("runnable接口"); //Comparator<T>接口 Comparator<Integer> comparator = (o1,o2)->Integer.compare(o1,o2); }
四大核心函数式接口
函数式接口 |
方法 |
|
void accept(T t); |
|
|
|
|
|
|
其他不常用接口:
Consumer接口示例
public class AnnotationTest { @Test public void test() { //普通写法,传入一个匿名接口实现类 useMoney(200, new Consumer<Double>() { @Override public void accept(Double aDouble) { System.out.println(aDouble); } });//200.0 //lambda表达式 useMoney(1000,o1-> System.out.println(o1));//1000.0 } public void useMoney(double money, Consumer<Double> con){ con.accept(money); } }
在方法中使用Consumer接口作为参数,处理单个值。
Predicate接口示例
public class AnnotationTest { @Test public void test() { //过滤掉List中包含"长"的字符串 List<String> lists = filterString(Arrays.asList("长路", "林儿", "长宏"), o1 -> !o1.contains("长")); System.out.println(lists);//[林儿] } //使用Predicate接口方法来过滤list集合中的元素 public List<String> filterString(List<String> list, Predicate<String> predicate){ ArrayList<String> lists = new ArrayList<>(); for (String s : list) { if(predicate.test(s)) lists.add(s); } return lists; } }
Predicate接口中的方法对其参数值进行判断返回一个布尔值。
1.3、方法引用与构造器引用
方法引用
何时使用:当你要对一个抽象方法进行Lambda体的操作时,若是有其他已实现的方法参数列表及返回值与其保持一致时,那么就可以使用方法引用,在执行抽象方法时会到引用的方法中依次执行。
三种使用方式:通过::隔开
对象::实例方法名
类::静态方法名
类::实例方法名
例1:Consumer接口示例
//示例 @Test public void test() { //Consumer<Integer> consumer = o -> System.out.println(o); Consumer<Integer> consumer = System.out::println; consumer.accept(123);//123 } //看一下Consumer接口的方法 void accept(T t); //再看一下System.out的println()方法 public void println(Object x) { String s = String.valueOf(x); synchronized (this) { print(s); newLine(); } }
可以看到Consumer接口的方法参数类型与返回值同println()一致,将println()方法体放置到accept()中。
例2:Comparator接口示例
@Test public void test() { //Comparator<Integer> comparator = (o1,o2) -> Integer.compare(o1,o2); Comparator<Integer> comparator = Integer::compare; System.out.println(comparator.compare(12, 23));//-1 } //看一下Comparator的抽象方法 int compare(T o1, T o2); //再看下Integer的compareTo()方法 public int compareTo(Integer anotherInteger) { return compare(this.value, anotherInteger.value); }
在执行comparator实例的方法compare()实际上会到Integer的指定方法compareTo()中执行。
构造器引用
格式:类名::new
使用说明:要求构造器的参数列表与接口抽象方法中的参数列表一致,且方法的返回值为构造器对应的对象。
示例:
@Test public void test() { //Function<String,String> function = n -> new String(n); Function<String,String> function = String::new; System.out.println(function.apply("changlu"));//changlu } //说明:看下Function接口的方法,上面泛型设置T为String,R为String(即返回值为String) R apply(T t); //String::new实际如下: String apply(String t){ return new String(t); }
针对于数组引用示例:
@Test public void test() { //Function<Integer,String[]> function = n -> new String[n]; Function<Integer,String[]> function = String[]::new; System.out.println(function.apply(5).length);//5 }
注意这里的第一个参数为Integer型,来表示开辟数组的个数。