前言
在详解Java8特性之Lambda表达式 中我介绍了Java8中的Lambda表达式,通过Lambda表达式我们就可以快速地创建一个函数式接口的实例,可以说是大大简便了我们开发。你以为只能做到这么简便了嘛,big big wrong,大大的错,还可以再简便一点。有请我们今天的主角—方法引用
在学习这个之前的得确保你已经学习过Lambda表达式了,因为这个是用在Lambda表达式的。
方法引用
方法引用如Lambda表达式一样也是一个语法糖,可以用来简化开发。
在我们使用Lambda表达式的时候,”->”右边部分是要执行的代码,即要完成的功能,可以把这部分称作Lambda体。有时候,当我们想要实现一个函数式接口的那个抽象方法,但是已经有类实现了我们想要的功能,这个时候我们就可以用方法引用来直接使用现有类的功能去实现。
这么说有点绕,直接来看例子吧
有个函数式接口Consumer
,里面有个抽象方法accept
能够接收一个参数但是没有返回值,这个时候我想实现accept
方法,让它的功能为打印接收到的那个参数,那么我可以使用Lambda表达式这么做
Consumer<String> consumer = str -> System.out.println(str);
consumer.accept("This is Major Tom");
但是其实我想要的这个功能PrintStream
类(也就是System.out
的类型)的println
方法已经实现了,在上面我就是使用它来完成我想要的功能,其实这一步还可以再简单点,如
Consumer<String> consumer = System.out::println;
consumer.accept("This is Major Tom");
看,是不是又简单了一点呢,这就是方法引用的一个例子~
那为什么可以这么用呢?
大家可以发现上面的accept
方法跟println
方法是不是很像,都是接收一个参数类型为String
参数,并且无返回值。
这就是方法引用的规定,实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!至于返回值就不作要求。
怎么用
现在就来具体介绍一下使用方法引用的语法
先来说一下方法引用使用到的操作符“::”,这个操作符把方法引用分成两边,左边是类名或者某个对象的引用,右边是方法名或者是“new”(构造器引用时用到)
有以下3种引用
- 引用方法
- 引用构造器
- 引用数组
可以发现,方法引用可以引用的不止是方法哟,就像TCP/IP协议族并不只有TCP和IP协议。
现在就来分别介绍一下这些引用
引用方法
引用方法有下面几种方式
- 对象引用::实例方法名
- 类名::静态方法名
- 类名::实例方法名
对象引用::实例方法名
//Consumer<String> consumer = x -> System.out.println(x);
Consumer<String> consumer = System.out::println;
consumer.accept("This is Major Tom");
System.out就是一个PrintStream
类型的对象引用,而println
则是一个实例方法名,需要注意的是没有括号的哟。其中Consumer
是Java内置函数式接口,下面的例子用到的都是Java内置函数式接口。Consumer
中的唯一抽象方法accept
方法参数列表与println
方法的参数列表相同,都是接收一个String
类型参数。
类名::静态方法名
//Function<Long, Long> f = x -> Math.abs(x);
Function<Long, Long> f = Math::abs;
Long result = f.apply(-3L);
Math
是一个类而abs
为该类的静态方法。Function
中的唯一抽象方法apply
方法参数列表与abs
方法的参数列表相同,都是接收一个Long
类型参数。
类名::实例方法名
若Lambda表达式的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,就可以使用这种方法,如
//BiPredicate<String, String> b = (x,y) -> x.equals(y);
BiPredicate<String, String> b = String::equals;
b.test("abc", "abcd");
String
是一个类而equals
为该类的定义的实例方法。BiPredicate
中的唯一抽象方法test
方法参数列表与equals
方法的参数列表相同,都是接收两个String
类型参数。
引用构造器
在引用构造器的时候,构造器参数列表要与接口中抽象方法的参数列表一致,格式为 类名::new。如
//Function<Integer, StringBuffer> fun = n -> new StringBuffer(n);
Function<Integer, StringBuffer> fun = StringBuffer::new;
StringBuffer buffer = fun.apply(10);
Function
接口的apply
方法接收一个参数,并且有返回值。在这里接收的参数是Integer
类型,与StringBuffer
类的一个构造方法StringBuffer(int capacity)
对应,而返回值就是StringBuffer
类型。上面这段代码的功能就是创建一个Function
实例,并把它apply
方法实现为创建一个指定初始大小的StringBuffer
对象。
引用数组
引用数组和引用构造器很像,格式为 类型[]::new,其中类型可以为基本类型也可以是类。如
// Function<Integer, int[]> fun = n -> new int[n];
Function<Integer, int[]> fun = int[]::new;
int[] arr = fun.apply(10);
Function<Integer, Integer[]> fun2 = Integer[]::new;
Integer[] arr2 = fun2.apply(10);