java 8_ 05,Java 8 - 05 方法引用

文章目录

Pre

方法引用

如何构建方法引用

指向静态方法的方法引用

指向任意类型实例方法的方法引用

指向现有对象的实例方法的方法引用

构造函数引用

自定义构造函数引用

2f71337dd9ae81889ce75ea63e654bba.png

Pre

先来看段代码

Comparator enginnerComparator = new Comparator() {

@Override

public int compare(Enginner o1, Enginner o2) {

return o1.getJob().compareTo(o2.getJob());

}

};

enginnerComparator.compare(new Enginner("Java",18),new Enginner("Go",10));

List enginnerList = Arrays.asList(new Enginner("Java",18),new Enginner("Go",10));

System.out.println("0enginnerList:" + enginnerList);

enginnerList.sort(enginnerComparator);

System.out.println("1enginnerList:" + enginnerList);

排序嘛 ,是不是很长

使用方法引用,一行代码搞定,如下:

enginnerList.sort(Comparator.comparing(Enginner::getJob));

System.out.println("2enginnerList:" + enginnerList);

d4761a5b6ec2b81853671df8a1c491a0.png

方法引用让你可以重复使用现有的方法定义,并像Lambda一样传递它们。在一些情况下比起使用Lambda表达式, 更易读 。上面的栗子就是借助了Java 8 API ,用方法引用写的一个排序的例子。

方法引用

方法引用可以被看作仅仅调用特定方法的Lambda的一种快捷写法。它的基本思想是,如果一个Lambda代表的只是“直接调用这个方法”,那最好还是用名称来调用它,而不是去描述如何调用它。

实际上,方法引用就是让你根据已有的方法实现来创建Lambda表达式。 显式地指明方法的名称,代码的可读性会更好 。

当你需要使用方法引用时,目标引用放在分隔符 :: 前,方法的名称放在后面

Enginner::getJob

就是引用了 Enginner类中定义的方法 getJob 。 请记住,不需要括号,因为你没有实际调用这个方法。

方法引用就是Lambda表达式 (Enginnera) -> a.getJob() 的快捷写法

再来看几个等效的例子 加深下印象

(Enginner a) -> a.getJob() 等价于 Enginner ::getJob

做下实验

public class MethodReferrenceDemo {

public static R doSomething(T t, Function f) {

return f.apply(t);

}

public static void main(String[] args) {

Function ef = (Enginner e) -> e.getJob();

System.out.println(doSomething(new Enginner("Java", 18), ef));

Function ef2 = Enginner::getJob;

System.out.println(doSomething(new Enginner("Java", 18), ef2));

}

}

70bc3934cbcee9cca6120acbbb3af2a9.png

还比如

() -> Thread.currentThread().dumpStack() 等价于 Thread.currentThread()::dumpStack

(str, i) -> str.substring(i) 等价于 String::substring

(String s) -> System.out.println(s) 等价于 System.out::println

我们可以把方法引用看作针对仅仅涉及单一方法的Lambda的语法糖,因为你表达同样的事情

时要写的代码更少了。

如何构建方法引用

方法引用主要有三类。

指向静态方法的方法引用

举个例子 : Integer 的 parseInt 方法,写作 Integer::parseInt

Function stringIntegerFunction2 = (String s) -> Integer.parseInt(s);

Function stringIntegerFunction3 = Integer::parseInt;

System.out.println(stringIntegerFunction2.apply("18"));

System.out.println(stringIntegerFunction3.apply("18"));

指向任意类型实例方法的方法引用

举个例子 : String 的 length 方 法 , 写 作 String::length

Function stringIntegerFunction = (String s) -> s.length();

Function stringIntegerFunction1 = String::length;

System.out.println(stringIntegerFunction.apply("abc"));

System.out.println(stringIntegerFunction1.apply("abc"));

类似于 String::length 方法引用的思想就是你在引用一个对象的方法,而这个对象本身是Lambda的一个参数。

例如,Lambda表达式 (String s) -> s.toUppeCase() 可以写作 String::toUpperCase

指向现有对象的实例方法的方法引用

假设你有一个局部变量 eng用于存放 Enginner 类型的对象,它支持实例方法 getValue ,那么你就可以写 eng::getValue

这种写法是我们在Lambda中调用一个已经存在的外部对象中的方法。 例如,Lambda表达式()->eng.getValue() 可以写作 eng::getValue 。

再来看几个例子, 将Lambda表达式重构为等价的方法引用

lambda : args -> ClassName.staticMethod(args)

等价于 (1)

方法引用: className::staticMethod

lambda : (arg0 ,rest)-> argo.instanceMethod(rest) (arg0是ClassName类型的)

等价于 (2)

方法引用: ClassName::instanceMethod

lambda : (args)-> expr.instanceMethod(args)

等价于

方法引用: expr::instanceMethod

请注意,还有针对构造函数、数组构造函数和父类调用(super-call)的一些特殊形式的方法引用。

比方说你想要对一个字符串的 List 排序,忽略大小写。 List 的 sort 方法需要一个 Comparator 作为参数 。 我们知道 Comparator 描述了 一个具有 (T, T) -> int 签名的函数描述符。你可以利用 String 类中的 compareToIgnoreCase方法来定义一个Lambda表达式。

List list = Arrays.asList("apple","pear","banana");

list.sort((s1,s2)-> s1.compareToIgnoreCase(s2));

Lambda表达式的签名与 Comparator 的函数描述符兼容。利用前面所述的方法,这个例子可以使用方法引用改成下面的样子

list.sort(String::compareToIgnoreCase);

请注意,编译器会进行一种与Lambda表达式类似的类型检查过程,来确定对于给定的函数式接口,这个方法引用是否有效:方法引用的签名必须和上下文类型匹配

来个小测验吧

测验:方法引用

下列Lambda表达式的等效方法引用是什么?

(1) Function stringToInteger =

(String s) -> Integer.parseInt(s);

(2) BiPredicate, String> contains =

(list, element) -> list.contains(element);

答案如下。

(1) 这个Lambda表达式将其参数传给了 Integer 的静态方法 parseInt 。这种方法接受一

个需要解析的 String ,并返回一个 Integer 。

因此,可以使用 Lambda表达式调用静态方法来重写Lambda表达式,如下所示:

Function stringToInteger = Integer::parseInt;

(2) 这个Lambda使用其第一个参数,调用其 contains 方法。由于第一个参数是 List 类型

的,你可以使用刚才的(2) 如下:

BiPredicate, String> contains = List::contains;

这是因为,目标类型描述的函数描述符是 (List,String) -> boolean ,而

List::contains 可以被解包成这个函数描述符。

构造函数引用

对于一个现有构造函数,我们可以利用它的名称和关键字 new 来创建它的一个引用:ClassName::new 。它的功能与指向静态方法的引用类似。

例如,假设有一个构造函数没有参数。它适合 Supplier 的签名 () -> Enginner。

Enginner的构造函数

ccc5b09e46b681166daa93815150c972.png

我们可以这样做:

// 构造函数引用指向默认Enginner的构造函数

Supplier supplier = Enginner::new;

// 调用 Supplier 的 get 方法 将产生一个新的 enginner

Enginner enginner = supplier.get();

等价于

Supplier s = () -> new Enginner();// 利用默认构造函数创建 Enginner的Lambda表达式

Enginner supplier2 = s.get();// 调用 Supplier 的 get 方法 将产生一个新的 Enginner

如果Enginner构造函数的签名是 Enginner(String job) ,那么它就适合 Function 接口的签

名,我们可以这样写:

// 指向 Enginner(String job) 的构造函数引用

Function f2 = Enginner::new;

// 调用该 Function 函数的 apply 方法,并给出职位,将产生一个 Enginner

Enginner e2 =f2.apply("Java");

System.out.println(e2.getJob());

如果你有一个具有两个参数的构造函数 Enginner(String job, Integer age) ,那么

它就适合 BiFunction 接口的签名 . 两个参数的 使用BiFunction 即可 (Bi = Binary )

BiFunction f3 = Enginner::new;

Enginner e4 = f3.apply("Java",18);

System.out.println(e4 .getJob() + " - " + e4.getAge());

上面是使用方法引用,如果直接用lambda呢? 如下

BiFunction f4 = (str , age)-> new Enginner(str, age);

Enginner ee =f4.apply("Go", 10);

System.out.println(ee .getJob() + " - " + ee.getAge());

91760a94d73dd29b9876b9ba43cafe95.png

自定义构造函数引用

上面的栗子我们将有零个、一个、两个参数的构造函数转变为构造函数引用。那要怎么样才能对具有三个参数的构造函数,比如 Color(int, int, int), 使用构造函数引用呢?

构造函数引用的语法是 ClassName::new ,那么在这个例子里面就是Color::new 。但是你需要与构造函数引用的签名匹配的函数式接口。但是语言本身并没有提供这样的函数式接口,你可以自己创建一个:

public interface TriFunction{

R apply(T t, U u, V v);

}

现在你可以像下面这样使用构造函数引用了:

TriFunction colorFactory = Color::new;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值