JDK8新特性(二)-方法引用

参考https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

方法引用

上一篇说了lambda表达式,主要是用来简化代码,代替匿名内部类的。那这篇说的方法引用是用来在特定场景下替代lambda表达式的,目的也是简化代码。 还是考虑Person这个类:

public class Person {

public enum Sex {
    MALE, FEMALE
}

String name;
LocalDate birthday;
Sex gender;
String emailAddress;

public int getAge() {
    // ...
}

public Calendar getBirthday() {
    return birthday;
}    

public static int compareByAge(Person a, Person b) {
    return a.birthday.compareTo(b.birthday);
}}

考虑一个 Person数组,现在要根据年龄对其排序,普通写法如下:

Person[] rosterAsArray = roster.toArray(new Person[roster.size()]);

class PersonAgeComparator implements Comparator {
public int compare(Person a, Person b) {
return a.getBirthday().compareTo(b.getBirthday());
}
}

Arrays.sort(rosterAsArray, new PersonAgeComparator());

因为Comparator接口是函数式接口,所以PersonAgeComparator类可以省去,用lambda表达式代替:

Arrays.sort(rosterAsArray,
(Person a, Person b) -> {
return a.getBirthday().compareTo(b.getBirthday());
}
);

或者直接调用Person类的compareByAge方法:

Arrays.sort(rosterAsArray,
(a, b) -> Person.compareByAge(a, b)
);

因为lambda表达式调用了已经存在的方法,所以可以使用更简化的方法引用替代:

Arrays.sort(rosterAsArray, Person::compareByAge);

Person::compareByAge与(a, b) -> Person.compareByAge(a, b)在语义上是等价的。都有如下特性:

  • 参数列表与函数式接口Comparator的方法compare(Person, Person)保持一致
  • 都调用了Person.compareByAge

几种方法引用的类型

类型

示例

引用静态方法

ContainingClass::staticMethodName

引用某对象的实例方法

containingObject::instanceMethodName

引用某类对象的实例方法

ContainingType::methodName

引用构造函数

ClassName::new

引用静态方法

上文的Person::compareByAge就是个例子。

引用某对象的实例方法

直接看代码:

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);
myComparisonProvider::compareByName”就是调用myComparisonProvider这个对象的compareByName方法。

引用某个类的实例的实例方法

这样翻译有点绕,直接看代码更清楚:

String[] stringArray = { “Barbara”, “James”, “Mary”, “John”,
“Patricia”, “Robert”, “Michael”, “Linda” };
Arrays.sort(stringArray, String::compareToIgnoreCase);

“String::compareToIgnoreCase”这一句接受两个参数,假定为(String a, String b),执行结果就相当于a.compareToIgnoreCase(b)。

引用构造函数

引用构造函数实例如下:

public static <T, SOURCE extends Collection, DEST extends Collection> DEST transferElements(
SOURCE sourceCollection,
Supplier collectionFactory) {

DEST result = collectionFactory.get();
for (T t : sourceCollection) {
    result.add(t);
}
return result;

}

上面代码中的transferElements方法是对集合进行复制。Supplier也是JDK8新增的函数式接口,源码如下,很简单:

@FunctionalInterface
public interface Supplier {
T get();
}

下面使用lambda表达式把list转换为hashset:

Set rosterSetLambda =
transferElements(roster, () -> { return new HashSet<>(); });

可以使用构造方法引用替换掉lambda表达式:

Set rosterSet = transferElements(roster, HashSet::new);

上面的语句没有指定HashSet结合中的具体元素类型,编译器会推断出应该是Person类型,当然,也可以指定明确指定集合中的元素类型,像下面这样:

Set rosterSet = transferElements(roster, HashSet::new);