JDK8的lambda表达式方法引用
Posted chenloveslife
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK8的lambda表达式方法引用相关的知识,希望对你有一定的参考价值。
(部分转自:https://www.cnblogs.com/xiaoxi/p/7099667.html)
1. lambda表达式
以前定义一个Thread:
1 final int i = 0;
2 new Thread(new Runnable() {
3 @Override
4 public void run() {
5 System.out.println("i = " + i);
6 }
7 }).start();
lambda表达式定义:
1 int i = 0;
2 new Thread(() -> System.out.println("i = " + i));
概述:lambda表达式除了使代码简洁外,还为了消除单方法接口实现的匿名内部类。
结构:
注意:
- lambda要求实现的接口中只有一个方法(该接口称为函数式接口),可以知道如果要得到一个接口的对象,则需要创建一个类并实现接口再实例化对象,而lambda则可以直接提供对象。
- 参数体内如果只有一个参数则可以省略小括号,方法体内如果只有一条语句则可以省略花括号。
- 参数体内可以省略数据类型,编译器可以推导出参数类型,例如:(first, second)-> {first.length() - second.length()}。
- 方法体内必须有return含义,即可以显式地return或隐式地return(编译器会根据上下文推导得出return返回的数据类型),例如上。
- lambda内部引用外部的变量,该变量必须是最终变量,即初始化后不再为它赋新值。
2. 方法引用
方法引用是直接访问类或者实例的方法、类方法、静态方法;提供了一种引用而不执行方法的方式;计算时,方法引用会创建函数式接口的一个实例。
方法引用等价于lambda表达式,只是lambda表达式更为简洁的写法,当lambda表达式只是执行一个方法调用时则不用lambda。lambda中如果参数和方法内使用的参数一致即可在方法引用中忽略参数。
// lambda写法:
Arrays.sort(stringsArray,(s1,s2)->s1.compareToIgnoreCase(s2));
// 方法引用写法
Arrays.sort(stringsArray, String::compareToIgnoreCase);
详细的例子,首先定义一个Person类,如下:
package com.demo.model;
import java.time.LocalDate;
public class Person {
public Person(String name, LocalDate birthday) {
this.name = name;
this.birthday = birthday;
}
String name;
LocalDate birthday;
public LocalDate getBirthday() {
return birthday;
}
public static int compareByAge(Person a, Person b) {
return a.birthday.compareTo(b.birthday);
}
@Override
public String toString() {
return this.name;
}
}
假设我们有一个Person数组,并且想对它进行排序,这时候,我们可能会这样写:
其中,Arrays类的sort方法定义如下:
public static <T> void sort(T[] a, Comparator<? super T> c)
这里,我们首先要注意Comparator
接口是一个函数式接口,因此我们可以使用Lambda表达式,而不需要定义一个实现Comparator
接口的类,并创建它的实例对象,传给sort方法。
使用Lambda表达式,未调用已存在的方法
public void test1() {
Person[] pArr = new Person[]{
new Person("003", LocalDate.of(2016,9,1)),
new Person("001", LocalDate.of(2016,2,1)),
new Person("002", LocalDate.of(2016,3,1)),
new Person("004", LocalDate.of(2016,12,1))};
//使用lambda表达式
Arrays.sort(pArr, (Person a, Person b) -> {
return a.getBirthday().compareTo(b.getBirthday());
});
System.out.println(Arrays.asList(pArr));
}
然而,在以上代码中,关于两个人生日的比较方法在Person类中已经定义了,因此,我们可以直接使用已存在的Person.compareByAge方法。
使用Lambda表达式,调用已存在的方法
public void test2() {
Person[] pArr = new Person[]{
new Person("003", LocalDate.of(2016,9,1)),
new Person("001", LocalDate.of(2016,2,1)),
new Person("002", LocalDate.of(2016,3,1)),
new Person("004", LocalDate.of(2016,12,1))};
//使用lambda表达式和类的静态方法
Arrays.sort(pArr, (a ,b) -> Person.compareByAge(a, b));
System.out.println(Arrays.asList(pArr));
}
因为这个Lambda表达式调用了一个已存在的方法,因此,我们可以直接使用方法引用来替代这个Lambda表达式。
使用方法引用
public void test3() {
Person[] pArr = new Person[]{
new Person("003", LocalDate.of(2016,9,1)),
new Person("001", LocalDate.of(2016,2,1)),
new Person("002", LocalDate.of(2016,3,1)),
new Person("004", LocalDate.of(2016,12,1))};
//使用方法引用,引用的是类的静态方法
Arrays.sort(pArr, Person::compareByAge);
System.out.println(Arrays.asList(pArr));
}
3. 方法引用几种类型:
(1) 静态方法引用
组成语法格式:ClassName::staticMethodName
我们前面举的例子Person::compareByAge就是一个静态方法引用。
例子:
String::valueOf 等价于lambda表达式 (s) -> String.valueOf(s)
Math::pow 等价于lambda表达式 (x, y) -> Math.pow(x, y);
(2) 对象方法引用
a.实例上的实例方法引用
组成语法格式:instanceReference::methodName
如下示例,引用的方法是myComparisonProvider 对象的compareByName方法。
class ComparisonProvider{
public int compareByName(Person a, Person b){
return a.getName().compareTo(b.getName());
}
public int compareByAge(Person a, Person b){
return a.getBirthday().compareTo(b.getBirthday());
}
}
ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);
b.超类上的实例方法引用
组成语法格式:super::methodName
方法的名称由methodName指定,通过使用super,可以引用方法的超类版本。
例子:
还可以捕获this 指针,this :: equals 等价于lambda表达式 x -> this.equals(x);
c.类型上的实例方法引用
组成语法格式:ClassName::methodName
注意:
若类型的实例方法是泛型的,就需要在::分隔符前提供类型参数,或者(多数情况下)利用目标类型推导出其类型。
静态方法引用和类型上的实例方法引用拥有一样的语法。编译器会根据实际情况做出决定。一般我们不需要指定方法引用中的参数类型,因为编译器往往可以推导出结果,但如果需要我们也可以显式在::分隔符之前提供参数类型信息。
例子:
String::toString 等价于lambda表达式 (s) -> s.toString()
这里不太容易理解,实例方法要通过对象来调用,方法引用对应Lambda,Lambda的第一个参数会成为调用实例方法的对象。
(3) 任意对象(属于同一个类)的实例方法引用
如下示例,这里引用的是字符串数组中任意一个对象的compareToIgnoreCase方法。
String[] stringArray = { "Barbara", "James", "Mary", "John", "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);
(4) 构造方法引用
构造方法引用又分构造方法引用和数组构造方法引用。
a. 构造方法引用(也可以称作构造器引用)
组成语法格式:Class::new
构造函数本质上是静态方法,只是方法名字比较特殊,使用的是new 关键字。
例子:
String::new, 等价于lambda表达式 () -> new String()
package com.demo; public interface MyFunc1 { MyClass func(int n); }
package com.demo; public class MyClass { private int val; MyClass(int v) { val = v; } MyClass(){ val = 0; } public int getValue() { return val; } }
package com.demo; public class ConstructorRefDemo { public static void main(String[] args) { MyFunc1 myClassCons = MyClass :: new; MyClass mc = myClassCons.func(100); System.out.println("val in mc is: " + mc.getValue()); } }
输出结果:
val in mc is: 100
b.数组构造方法引用
组成语法格式:TypeName[]::new
例子:
int[]::new 是一个含有一个参数的构造器引用,这个参数就是数组的长度。等价于lambda表达式 x -> new int[x]。
假想存在一个接收int参数的数组构造方法
IntFunction<int[]> arrayMaker = int[]::new;
int[] array = arrayMaker.apply(10) // 创建数组 int[10]
以上是关于JDK8的lambda表达式方法引用的主要内容,如果未能解决你的问题,请参考以下文章