在上一篇的Java8新特性中我们聊过Lambda表达式,这一篇我们来看一下Java8中另一个特性:方法引用”
方法引用
在讲方法引用之前我们先看一个例子以及输出的结果:
public static void LambdaTest() {
List list = Arrays.asList("Lambdas",
"Default Method",
"Stream API",
"Date and Time API");
list.forEach(n -> System.out.println(n));
list.forEach(System.out::println);
}
结果:
Connected to the target VM, address: '127.0.0.1:51219', transport: 'socket'
Lambdas
Default Method
Stream API
Date and Time API
Lambdas
Disconnected from the target VM, address: '127.0.0.1:51219', transport: 'socket'
Default Method
Stream API
Date and Time API
Process finished with exit code 0
我们可以看到他们的输出结果是一样的。第一种格式的输出我们在讲解Lambda的时候已经看到过,但是第二种你是否看过?同时你是知道::这个符号代表什么意思呢?我们慢慢来看:
今天要说的方法引用就是上面代码第二种格式,在上一篇关于Lambda的文章中,我们说Lambda在一定条件下能代替匿名类,但是有时候Lambda表达式中一些代码仅仅是调用了一个已存在的方法(可以理解成访问的类或者实例已经存在,而这个我们去调用它的方法或者构造方法)。我们可以使用形如System.out::println这种方式去代替n -> System.out.println(n);而这种特性就叫做方法引用。我们方法引用的操作符就是双冒号"::"。
此时你是不是很好奇我们什么时候使用方法引用呢?以及我们如何知道我们要用这用形式呢?
我们继续看一个例子:
先定义一个函数式接口:
@FunctionalInterface
interface RefenceTest {
public abstract String refence(String n);
}
一个引用方法(通过对象名引用成员方法,MethodRefence是方法所在的类名
PrintString和refencePlug是MethodRefence类的一个方法(虽然没带括号,下面我们会展示这个方法),这就好像是重写了上面接口中的refence方法):
public static void refenceImp() {
/*
通过对象名引用成员方法
*/
MethodRefence methodRefence = new MethodRefence();
PrintString(methodRefence::refencePlug);
}
MethodRefence就是这个方法所在的类名,而PrintString这个方法是这样的:
public static void PrintString(RefenceTest s) {
s.refence("每天学Java");
}
方法的参数中是我们的函数式接口的引用,如果不知道Java8方法引用的情况下,我们要想使用接口的方法,我们必须用一个实现它的类或者匿名类或者Lambda:
//匿名类
PrintString(new RefenceTest() {
@Override
public String refence(String n) {
System.out.println("每天学Java");
return "";
}
});
//Lambda
RefenceTest refenceTest = n -> n;
System.out.println(
refenceTest.refence("每天学Java")
);
但是我们上面说了,如果一个类的实例的方法存在,我们就可以通过方法引用实现相同的效果:
public String refencePlug(String n) {
System.out.println(n);
return "";
}
这个refencePlug方法我们就可以当成实例方法的存在(注意:它的返回类型要和函数式接口的返回相同,且不能是静态的,因为接口中的方法默认是abstract的,不能和static同时修饰)
在这种情况下:我们就可以使用:
public static void refenceImp() {
/*
通过对象名引用成员方法
*/
MethodRefence methodRefence = new MethodRefence();
PrintString(methodRefence::refencePlug);
}
public static void PrintString(RefenceTest s) {
s.refence("每天学Java");
}
public String refencePlug(String n) {
System.out.println(n);
return "";
}
如果这样一看,好像它比Lambda麻烦不少啊,我还要去自己写方法,Lambda直接就可以搞定了,我们要考虑以下几点,一.如果上面的方法是jar包中已经存在的方法,而不是你自己写的,你还觉得它麻烦嘛?二.如果这个接口的方法在几十个或者上百个类中使用过,你在每一个类中调用Lambda少写代码,还是定义一个类用于方法引用写的代码少呢?就像下面一样:
list.forEach(n -> System.out.println(n));
list.forEach(System.out::println);
那么说了上面的例子,我们现在来描述一下方法引用:当我们使用双冒号表示方法引用时候,就会创建一个函数式接口的实例(可以理解为实现了这个接口,并重写了它的方法的实例)。 简单地说,Java 8中,我们使用Lambda表达式创建匿名方法,有时候,我们的Lambda表达式可能仅仅调用一个已存在的方法,而不做任何其它事,对于这种情况,通过一个方法名字来引用这个已存在的方法会更加清晰。
不知道说了这些之后,你是否对于方法引用有一些理解,希望各位小伙伴可以自己敲代码体验以下。下面我附上自己写的几个小例子。
02
—
小例子
1.函数式接口的静态方法如何调用:
@FunctionalInterface
interface RefenceTest {
public abstract String refence(String n);
public static void staticMethod(String n){
System.out.println(n);
}
}
希望你们看到如何调用后,不要骂我骗子,你这分明是凑例子吗(嘿嘿,其实静态方法还用问怎么调用吗?不过不知道大家时候知道Java8中的接口允许写方法的)
public static void main(String[] args) {
RefenceTest.staticMethod("赶快来关注每天学Java吧");
}
2.通过类名引用静态方法(RefenceTest接口代码和上面相同,MethodRefence就是这个类的类名)
/*
通过类名引用静态方法
*/
public static String getSplit(RefenceTest refenceTest) {
refenceTest.refence("这里有许多Java相关的文章哦");
return "";
}
public static String SpiltPlug(String y) {
System.out.println(y);
return "";
}
public static void main(String[] args) {
getSplit(MethodRefence::SpiltPlug);
}
3. 通过类名引用成员方法,不用我强调类名和对象名的区别吧(类名 对象名=new 类名();)。这里我们虽然说是使用类名去引用成员方法,但是实际上我们还是传入了对象名,只是写的方式不一样而已
public void class0Method(String x) {
System.out.println(x);
}
public static void class0Me(RefenceTest2 refenceTest2,
MethodRefence methodRefence,
String x) {
refenceTest2.refence(methodRefence, x);
}
public static void main(String[] args) {
class0Me(MethodRefence::class0Method,
new MethodRefence(),
"我都标红了,你为啥子还不关注每天学Java"); }
好了,这篇文章就到这里结束了,大家有任何问题可以关注公众号留言哦。明天推送的是Effective Java第三版的条目十三:谨慎地重写 clone 方法。同时每天学Java小程序题库更新了:关于MQ的一些面试题(或者说是知识点)