Java8新特性:Lambda表达式与方法引用和构造器引用

方法引用类型

名字 语法 相应的Lambda表达式
静态 RefType::staticMethod (args) -> RefType.staticMethod(args)
绑定示例 expr::instMethod (args) -> expr.instMethod(args)
未绑定示例 RefType::instMethod (args0, rest) -> arg0.instMethod(rest)
构造器 ClsName::new (args) -> new ClsName(args)

1、类名::静态方法名

如果函数式接口的抽象方法的实现刚好可以通过调用一个静态方法来实现,那么就可以使用该类型的方法引用。例如,一个Integer类型集合,现在需要把它转换成对应的String类型的集合,就可以使用下面的代码:

List<Integer> list = Arrays.asList(1, 2, 3, 4);
List<String> strList = list.stream()
                        .map(String::valueOf)
                        .collect(Collectors.toList());

上面的map方法参数是Function类型的,把Integer类型转换成对应的String类型,Java中已经有现成的静态方法String.valueOf(int i)可以实现了,因此可以使用方法引用。更加的简洁。

2、对象名::实例方法名

如果函数式接口的抽象方法的实现刚好可以通过调用一个实例的实例方法来实现,那么就可以使用该类型的方法引用。下面给出一个例子: 

新建一个类:

public class Student {
    private String name;
    private int score;
    
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getScore() {
        return score;
    }
    public void setScore(int score) {
        this.score = score;
    }
    
    public int compareByScore(Student student1, Student student2) {
        return student1.getScore() - student2.getScore();
    }
    
    public int compareByName(Student student1, Student student2) {
        return student1.getName().compareToIgnoreCase(student2.getName());
    }
}

然后定义一个Student集合,让集合按照学生名字进行排序:

Student student1 = new Student("zhangsan", 10);
Student student2 = new Student("lisi", 90);
Student student3 = new Student("wangwu", 50);
Student student4 = new Student("zhaoliu", 40);

List<Student> students = Arrays.asList(student1, student2, student3, student4);

students.sort(student1::compareByName);  // 调用排序方法

students.forEach(s -> System.out.println(s.getName()));

这里因为Comparator接口的compare方法刚好可以使用Student实例的compareByName方法来实现,因此可以使用方法引用。

执行结果

lisi
wangwu
zhangsan
zhaoliu

也可以根据分数排序,调用方法compareByScore

3、类名::实例方法名

如果函数式接口的抽象方法的实现刚好可以由这样一个实例方法的调用来实现:抽象方法的第一个参数类型刚好是实例方法的类型,抽象方法剩余的参数恰好可以当做实例方法的参数。如果抽象方法的实现能由上面说的实例方法调用来实现的话,那么就可以使用该类型的方法引用。即调用的是Lambda表达式第一个参数的实例方法,Lambda表达式第一个参数以外的参数按顺序作为实例方法的参数传进去完成调用。

修改上面Student的方法,如下

public int compareByScore(Student student) {
    return this.getScore() - student.getScore();
}

public int compareByName(Student student) {
    return this.getName().compareToIgnoreCase(student.getName());
}

修改测试方法如下

students.sort(Student::compareByScore); // 调用排序方法

students.forEach(s -> System.out.println(s.getName() + ":" + s.getScore()));

执行结果

zhangsan:10
zhaoliu:40
wangwu:50
lisi:90

注意观察,sort中,传入的是Student类,并非对象引用

4、构造方法引用: 类名::new

构造器引用的创建方式语法类似于方法引用,只不过使用的关键字是new

例如,Button::new 表示Button类的构造器引用。对于拥有多个构造器的类,选择使用那个构造器取决于上下文。

可以使用数组类型来编写构造器引用。例如,int[]::new是一个含有一个参数的构造器引用,这个参数就是数组的长度。它等同于lambda表达式 x -> new int[x]。

数组构造器引用可以用来绕过Java中的一个限制。在Java中,无法构造一个泛型类型T的数组。表达式new T[n]是错误的,因为它会被擦除为 new Object[n]。假设我们希望构造一组按钮,Stream接口中有一个返回Object数组的toArray方法:

Object[] buttons = stream.toArray();

但是是返回Object[],我们想要的是Button[],stream API通过构造器引用解决了这个问题。它允许将Button[]::new传递给toArray方法: 

Button[] buttons = stream.toArray(Button[]::new)


赞(52) 打赏
未经允许不得转载:优客志 » JAVA开发
分享到:

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏