之前的文章已经介绍了函数式接口与lambda表达式,这篇文章主要学习下方法引用。使用方法引用,可以减少lambda表达式的书写,在Stream API中很常用。
一段代码对比下lambda与静态方法引用:
List<Integer> ids = Arrays.asList(1, 2, 5, 4, 3);
// 使用lambda表达式
Comparator<Integer> comparator1 = (a, b) -> a - b;
// 使用静态方法引用
Comparator<Integer> comparator2 = Integer::compare;
List<Integer> sorted1 = ids.stream().sorted(comparator1).collect(Collectors.toList());
List<Integer> sorted2 = ids.stream().sorted(comparator2).collect(Collectors.toList());
System.out.println(ids);// [1, 2, 5, 4, 3]
System.out.println(sorted1); // [1, 2, 3, 4, 5]
System.out.println(sorted2); // [1, 2, 3, 4, 5]
我们想实现整数list的排序,使用lambda我们还得自己编写一个Comparator对象(虽然也很简单),实际上JDK类库已经提供了类似的实现,我们通过Integer::compare就可以引用已经存在的方法。
JDK8中的方法引用分成4类:静态方法引用、实例方法引用、构造方法引用、以静态方式引用实例方法。
格式:ClassName::staticMethodName,比如:
String::valueOf 等价于lambda表达式 (s) -> String.valueOf(s)
Math::pow 等价于lambda表达式 (x, y) -> Math.pow(x, y)
@FunctionalInterface
public interface MyInterface {
public double calculate(double a, double b);
}
// 静态方法引用pow
MyInterface ins1 = Math::pow;
System.out.println(ins1.calculate(2, 4) == 16);
// 静态方法引用max
MyInterface ins2 = Math::max;
System.out.println(ins2.calculate(2, 4) == 4);
可以看到我们定义的函数式接口MyInterface,必须要与其引用的静态方法,具有同样的返回值和入参。
格式:instanceReference::methodName,比如:
str::toString 等价于lambda表达式 () -> str.toString()
str::concat 等价于lambda表达式 (another) -> str.concat(another)
@FunctionalInterface
public interface MyInterface {
// 与String.concat()同样的入参和返回值
public String transform(String input);
}
String content = "abc";
// 引用String类的实例方法
MyInterface ins1 = content::concat;
System.out.println(ins1.transform("def"));// abcdef
可以看到我们实现的效果:定义一个函数式接口,它的方法与原始的concat拥有同样的参数类型和返回值,相当于是给concat重新命名。
格式:ClassName::new,如果ClassName有多个构造函数,那么JDK会自动根据函数式接口的方法声明来决定到底使用哪儿一个构造函数。
public class Target {
public int attr = 0;
public Target() {
}
public Target(int b) {
this.attr = b;
}
}
@FunctionalInterface
public interface MyInterface {
public Target create(int value);
}
MyInterface ins = Target::new;
Target t = ins.create(1); // 可以自动推断使用的构造函数
System.out.println(t.attr);// 1
数组的构造函数与之类似,不过是构造函数有个参数(数组长度)。
@FunctionalInterface
public interface MyInterface {
public int[] create(int length);
}
MyInterface ins = int[]::new;
int[] array = ins.create(10);
System.out.println(array.length);// 10
不知道叫什么名字才合适,姑且这么叫吧。静态方法引用和类型上的实例方法引用拥有一样的语法,编译器会根据实际情况做出决定。
格式:ClassName::instanceMethod,比如:
String::toString 等价于lambda表达式 (String s) -> s.toString()
Lambda的第一个参数会成为调用实例方法的对象。
public class Target {
private int attr = 0;
public Target(int attr) {
this.attr = attr;
}
public int compareTo(Target another) {
return this.attr - another.attr;
}
public static int compare(Target one, Target another) {
return one.attr - another.attr;
}
}
// 函数式接口:实现Target对象比较
@FunctionalInterface
public interface MyInterface {
public int compare(Target one, Target another);
}
Target target1 = new Target(10);
Target target2 = new Target(100);
// 引用实例方法
MyInterface ins1 = Target::compare;
System.out.println(ins1.compare(target1, target2));// -90
// 引用静态方法
MyInterface ins2 = Target::compare;
System.out.println(ins2.compare(target1, target2));// -90
public class Person {
private String name;
private Date birthday;
public Person(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", birthday=" + birthday +
'}';
}
}
List<Person> persons = new ArrayList<>();
persons.add(new Person("c", "2017-01-01"));
persons.add(new Person("b", "2016-01-01"));
persons.add(new Person("a", "2015-01-01"));
现在我们想对persons集合按照name排序:
// 方式1
Collections.sort(persons, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getName().compareTo(o2.getName());
}
});
System.out.println(persons);
// 方式2
Collections.sort(persons, (o1, o2)->o1.getName().compareTo(o2.getName()));
System.out.println(persons);
// 方式3
Collections.sort(persons, Comparator.comparing(Person::getName));
System.out.println(persons);