秒懂Java之方法引用(method reference)详解

您所在的位置:网站首页 java调用其他方法 秒懂Java之方法引用(method reference)详解

秒懂Java之方法引用(method reference)详解

2024-02-24 11:14| 来源: 网络整理| 查看: 265

[版权申明]非商业目的注明出处可自由转载 出自:shusheng007

相关文章: 秒懂Java之深入理解Lambda表达式

文章目录 概述使用条件使用场景如何使用方法引用的类型调用类的静态方法调用传入的实例参数的方法调用已经存在的实例的方法调用类的构造函数 总结

概述

如果你对将Lambda表达式转换成对应的方法引用有疑惑的话,本文你值得一看。

方法引用(MethodReference)是Lambda表达式的另一种格式,在某些场景下可以提高代码的可读性,那么如何将一个Lambda表达式替换成MethodReference呢?有的同学说了,可以使用IDE协助转换,我只能说你太机智了,那这篇文章不是为你准备的。

使用条件

只可以替换单方法的Lambda表达式

什么意思呢 ?

例如下面这个Lambda表达式就不可以使用方法引用替换,因为其不是单方法的,有好几行呢。如果想要使用方法引用就需要将lambda结构体重构为一个方法。

Predicate p2 = integer -> { System.out.println("中国股市是吃屎长大的"); return TestUtil.isBiggerThan3(integer); };

下面这个就可以使用方法引用替换了

Predicate p2 = integer -> TestUtil.isBiggerThan3(integer); 使用场景

当使用方法引用替换Lambda表达式具有更好的可读性时,考虑使用。

如何使用

曾几何时,我对方法引用的理解很模糊,一般是使用IDE协助转换,但是转换后的写法很多我都不明白:

Lambda的参数哪去了?为什么::前面有的是类名称,有的是实例对象呢?不是只有静态方法::前面才使用类名吗,怎么有的实例方法也使用类名啊?为什么 ClassName::new 有时要求无参构造器,有时又要求有参构造器呢?构造器参数由什么决定呢?

结论其实显而易见了,我对方法引用的理解根本就是一团浆糊!如果你也有上面的疑问,也许应该接着往下看。

方法引用的类型

解决纷繁复杂信息的最好方式就是分类,这里也不例外。方法引用可以分为分四类,只要掌握了类型区别一切就变得易如反掌了。为行文方便,这里先列出要使用的类:

TestUtil里面有一个静态方法,一个实例方法。Student是一个普通实体类,具体如下代码所示

public class MethodReference { ... //示例类 public static class TestUtil { public static boolean isBiggerThan3(int input) { return input > 3; } public void printDetail(Student student) { System.out.println(student.toString()); } } public static class Student { private String name; private int age; public Student(String name, int age) { this.name = name; this.age = age; } public String getStatus(String thing) { return String.format("%d岁的%s正在%s", age, name, thing); } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } } } 调用类的静态方法

Lambda表达式的那个单方法是某个类的静态方法

有如下格式,args是参数,可以是多个,例如(a1,a2,a3)

lambda:

(args) -> Class.staticMethod(args)

method reference

Class::staticMethod

符合上面形式的调用,不管有多少参数,都省略掉,编译器自动会帮我们传入

实例:

public void testStaticMethodRef() { //匿名内部类形式 Predicate p1 = new Predicate() { @Override public boolean test(Integer integer) { return TestUtil.isBiggerThan3(integer); } }; //lambda表达式形式 Predicate p2 = integer -> TestUtil.isBiggerThan3(integer); //MethodReference形式 Predicate p3 = TestUtil::isBiggerThan3; Stream.of(1, 2, 3, 4, 5).filter(p3).forEach(System.out::println); }

其中isBiggerThan3 是TestUtil类的static方法。从上面的代码你可以清晰的看到,方法从匿名类到Lambda再到方法引用的演变。

调用传入的实例参数的方法

lambda:

(obj, args) -> obj.instanceMethod(args)

method reference

ObjectType::instanceMethod

看到我们lambda的入参obj了吗?它是一个类型,假设为ObjectType,的实例对象。然后再看lambda表达式,是在调用此实例obj的方法。这种类型的lambda就可以写成上面的形式了,看起来和静态方法那个一样。

我们来举个栗子:

public void testInstanceMethodRef1() { //匿名类 BiFunction f1 = new BiFunction() { @Override public String apply(Student student, String s) { return student.getStatus(s); } }; //lambda BiFunction f2 = (student, s) -> student.getStatus(s); //method reference BiFunction f3 = Student::getStatus; System.out.println(getStudentStatus(new Student("erGouWang", 18), "study", f3)); } private String getStudentStatus(Student student, String action, BiFunction biFunction) { return biFunction.apply(student, action); } 调用已经存在的实例的方法

lambda:

(args) -> obj.instanceMethod(args)

method reference

obj::instanceMethod

我们观察一下我们lambda表达式,发现obj对象不是当做参数传入的,而是已经存在的,所以写成方法引用时就是实例::方法.

举个栗子:

public void testInstanceMethodRef2() { TestUtil utilObj = new TestUtil(); //匿名类 Consumer c1 = new Consumer() { @Override public void accept(Student student) { utilObj.printDetail(student); } }; //Lambda表达式 Consumer c2 = student -> utilObj.printDetail(student); //方法引用 Consumer c3 = utilObj::printDetail; //使用 consumeStudent(new Student("erGouWang", 18), c3); } private void consumeStudent(Student student, Consumer consumer) { consumer.accept(student); }

可见utilObj对象是我们提前new出来的,是已经存在了的对象,不是Lambda的入参。

调用类的构造函数

lambda:

(args) -> new ClassName(args)

method reference

ClassName::new

当lambda中的单方法是调用某个类的构造函数,我们就可以将其写成如上形式的方法引用

举个栗子

public void testConstructorMethodRef() { BiFunction s1 = new BiFunction() { @Override public Student apply(String name, Integer age) { return new Student(name, age); } }; //lambda表达式 BiFunction s2 = (name, age) -> new Student(name, age); //对应的方法引用 BiFunction s3 = Student::new; //使用 System.out.println(getStudent("cuiHuaNiu", 20, s3).toString()); } private Student getStudent(String name, int age, BiFunction biFunction) { return biFunction.apply(name, age); }

上面代码值得注意的就是,Student类必须有一个与lambda入参相匹配的构造函数。例如此例中,(name, age) -> new Student(name, age); 需要两个入参的构造函数,为什么呢?

因为我们的lambda表达式的类型是 BiFunction,而其正是通过两个入参来构建一个返回的。其签名如下:

@FunctionalInterface public interface BiFunction { R apply(T t, U u); ... }

通过入参(t,u)来生成R类型的一个值。

我们可以写一个如下的方法引用

Function s4 = Student::new;

但是IDE就会报错,提示我们的Student类没有对应的构造函数,我们必须添加一个如下签名的构造函数才可以

public Student(String name) { this.name = name; } 总结

熟悉了以上四种类型后,方法引用再也难不住你了!留个作业:

Consumer consumer1 = new Consumer() { @Override public void accept(String s) { System.out.println(s); } }; //lambda表达式 Consumer consumer2 = ; //方法引用 Consumer consumer3 = ;

上面代码中的consumer2和consumer3分别是什么呢?其属于方法引用的哪一类呢?请在评论区提交答案。

古之立大事者,不惟有超世之才,亦必有坚忍不拔之志。——苏轼

你可以从Gitbub上找到本文源码:ToMasterJava

引用文章: https://www.codementor.io/@eh3rrera/using-java-8-method-reference-du10866vx



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3