Java8新特性之方法引用

日常开发中,经常使用到Lambda表达式,例如:

public static void main(String[] args) {

    List<Integer> list = Arrays.asList(1, 5, 10, 4, 2);

    // 打印列表中的每一个数字

    list.forEach((x) -> System.out.println(x));

}

其中(x) -> System.out.println(x)就是使用的Lambda表达式。Lambda表达式可以分为三部分:

左括号:Lambda的形参列表,对应接口的抽象方法的形参列表。

箭头:Lambda的操作符,可以理解为参数列表和Lambda体的分隔符。

Lambda体:即对应接口中的抽象方法的实现方法体。

你是否发现,上述例子的Lambda表达式的Lambda体仅仅调用一个已存在的方法,而不做任何其它事。对于这种情况,通过一个方法名字来引用这个已存在的方法会更加清晰。所以,方法引用应运而生,方法引用是一个更加紧凑,易读的Lambda表达式,它是Lambda表达式的另外一种表现形式,方法引用的操作符是双冒号 :: 。

使用了方法引用,上述例子编写如下,变得更加紧凑,易读了。

public static void main(String[] args) {

    List<Integer> list = Arrays.asList(1, 5, 10, 4, 2);

    // 打印列表中的每一个数字

    list.forEach(System.out::println);

}

二 方法引用

方法引用就是通过方法的名字来指向一个方法。它可以使语言的构造更紧凑简洁,减少冗余代码。方法引用的操作符是双冒号 :: 。方法引用有如下几种分类:

类型 语法 Lambda表达式

静态方法引用 类名::静态方法名 (args) -> 类名.静态方法名(args)

实例方法引用 实例::实例方法名 (args) -> 实例.实例方法名(args)

对象方法引用 类名::对象方法名 (inst,args) -> 类名.对象方法名(args)

构建方法引用 类名::new (args) -> new 类名(args)

三 实践

以下例子主要借用学生类来演示,学生类定义如下:

public class Student {

    private String name;

    private Integer age;

    public static int compareByAge(Student s1, Student s2) {

        return s1.age.compareTo(s2.age);

    }

    // 省略属性get/set方法

}

3.1 静态方法引用

现假设有50个学生,存放在一个list列表中,现需要对年龄进行从小到大排序。我们一般会写一个比较器进行排序,如下:

package com.nobody;

import java.util.ArrayList;

import java.util.Comparator;

import java.util.List;

/**

* @Description

* @Author Mr.nobody

* @Date 2021/3/7

* @Version 1.0

*/

public class Test {

    public static void main(String[] args) {

        List<Student> list = new ArrayList<>();

        // 添加元素省略,测试可自行添加

        // 排序

        list.sort(new StudentAgeComparator());

    }

// 对学生年龄的比较器

    static class StudentAgeComparator implements Comparator<Student> {

        public int compare(Student s1, Student s2) {

            return s1.getAge().compareTo(s2.getAge());

        }

    }

}

我们发现,List的sort方法接受的参数Comparator是一个函数式接口,则可以用Lambda表达式改为如下形式:

list.sort((s1, s2) -> s1.getAge().compareTo(s2.getAge()));

我们又发现,Student类有个静态方法compareByAge,其功能和上述Lambda表达式一样,所以我们可以将以上Lambda表达式改为如下形式:

list.sort((s1, s2) -> Student.compareByAge(s1, s2));

可以看出,最终的Lambda表达式是调用Student类的一个方法,所以,根据静态方法引用规则,可改为如下形式:

list.sort(Student::compareByAge);

3.2 实例方法引用

即引用已经存在的实例的方法。静态方法引用类无需实例化,直接用类名来调用,而实例方法引用是要先实例化对象。

如果将Student类的静态方法compareByAge改为非静态方法,即:

public int compareByAge(Student s1, Student s2) {

    return s1.age.compareTo(s2.age);

}

则可通过如下方式对学生数组进行排序:

list.sort(new Student()::compareByAge);

3.3 对象方法引用

如果Lambda表达式的参数列表中,第一个参数是实例方法的调用者对象,第二个参数是实例方法的参数时,可使用对象方法引用。例如,String的equals()方法:

public static void main(String[] args) {

    BiPredicate<String, String> bp1 = (x, y) -> x.equals(y);

    boolean test1 = bp1.test("Mr.nobody", "Mr.anybody");

    System.out.println(test1);


    BiPredicate<String, String> bp2 = String::equals;

    boolean test2 = bp2.test("Mr.nobody", "Mr.anybody");

    System.out.println(test2);

}

再比如,我们在Student类定义如下实例方法,方法中用到了Srudent对象的toString方法。

public class Student {

    private String name;

    private Integer age;

    public static int compareByAge(Student s1, Student s2) {

        return s1.age.compareTo(s2.age);

    }

    // 省略属性get/set方法

    public void whoIam() {

        System.out.println("I am " + this.toString());

    }


    @Override

    public String toString() {

        return "Student{" +

                "name='" + name + '\'' +

                ", age=" + age +

                '}';

    }

}

public static void main(String[] args) {

    List<Student> list = new ArrayList<>();

    list.forEach(Student::whoIam);

}

3.4 构造方法引用

注意,引用的构造方法的参数列表要和函数式接口中抽象方法的参数列表保持一致。

public static void main(String[] args) {

    Supplier<Student> studentSupplier1 = () -> new Student();

    Student student1 = studentSupplier1.get();


    // 构造方法引用

    Supplier<Student> studentSupplier2 = Student::new;

    Student student2 = studentSupplier2.get();

}

引用数组和引用构造器很像,格式为类型[]::new,等价于lambda 表达式 x -> new int[x]。其中类型可以为基本类型也可以是类。

public static void main(String[] args) {


    Function<Integer, Student[]> studentFunction = Student[]::new;

    Student[] students = studentFunction.apply(10);

}

深圳网站建设www.sz886.com

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,692评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,482评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,995评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,223评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,245评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,208评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,091评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,929评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,346评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,570评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,739评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,437评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,037评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,677评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,833评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,760评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,647评论 2 354

推荐阅读更多精彩内容