[2021.02.22][Java语言学习]0.3.函数引用

1. 函数引用

在使用lambda表达式的时候,表达式中包含的是不同的语句。但也有的时候,lambda表达式中只有一个调用函数的语句。例如(a)->System.out.println(a)。这种情况如下,就可以通过函数引用的形式(即System.out::println)使得程序更加的简洁。

需要记住的是,lambda表达式是用来表示接口函数的,一个lambda表达式可以替换匿名接口类的一个函数,那么函数引用也是,不能随便用,需要用来替换接口的实现。

2. 举例

有一个抽象学生类,包含年龄,姓名,性别三个变量,提供了setter/getter方法,以及比较姓名字母排序的方法。

在程序中,根据学生类创建出了多个学生,并保存在数组中。如何使用sort(T[] a, Comparator<? super T> c)对数组中的学生根据姓名字母进行排序。

第一个方法是创建出一个Comparator接口的匿名类,Comparator本身是一个接口,在匿名类中对接口方法进行实现。

Arrays.sort(students, new Comparator<Student>() {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getName().compareToIgnoreCase(o2.getName());
    }
});

第二个方法,既然接口只实现了一个方法,那么就可以使用lambda表达式,并且在Lambda表达式中无需指定ab的类型,编译器会推断出lambda表达式中的变量属于哪个类型。

Arrays.sort(students, (a, b) -> a.getName().compareToIgnoreCase(b.getName()));

第三个方法,在ambda表达式将比较方法写了一遍,但其实Student类中已经有了比较方法,无须再写一遍,所以可以将这个繁琐(这一句可能不算繁琐,但是有很多语句的时候,重复写不免就比较繁琐了)的比较方法使用Student中提供的实现。

Arrays.sort(students, (a, b) -> Student.compareByName(a,b));

第四个方法,此时如果使用的是idea等集成开发环境,开发环境就会提示你用函数引用了,这是因为lambda表达式中只有一个语句而且是调用的Student中的一个static函数,可以省略成函数引用的形式。下面的语句Student::compareByName就是函数引用的一种形式--引用静态函数。

Arrays.sort(students, Student::compareByName);

3. 函数引用的类型和对应形式

在四种情况下可以采用函数引用,也就是有四种引用情形和形式,表格如下:

函数引用情形 函数引用形式
引用类中的静态函数 ClassName::StaticMethod
引用某个实例的函数 instanceName::Method
引用某个类的任意实例的函数 ClassName::Method
引用类中的构造函数 ClassName::new

其中前两个很好理解,而第三个和第四个有点新奇。

第三个我寻思着不就是类中的非静态函数吗?答案为是且不是,确实是非静态函数,但是这种方法引用是有限制的。主要是对于引用方法参数和接口方法参数的限制(具体可以查看这篇博文),限定条件为:

  • 接口函数必须要比引用函数多一个参数
  • 并且第一个参数为引用函数所在类或者父类的实例即第一个参数为ClassName instanceName的形式或者ParentofClassName instanceName

在示例中,有Arrays.sort(students, Student::compareByNameInInstance);这样一句话,其中Arrays.sort()的参数中第二个参数所实现的接口方法int compare(T o1, T o2)有两个参数,分别是compare(),类型为Student(此处程序会将泛型编译为Student类型),这就可以发现①比compareByNameInInstance(Student student)的参数多一个,②compare第一个参数Student o1为Student类的一个实例。

第四个构造函数使用方法引用不禁引出一个问题,如果构造函数需要参数,那么参数怎么表示?

在使用引用函数的时候不需要表示参数。回忆第一段lambda的使用位置,需要知道的是使用引用构造函数是在对某一个接口进行实例化,此时构造函数是接口函数中的语句,那么这个接口函数有几个参数,实例化的时候就会调用相应参数个数的构造函数。例如下面代码中,接口为StudentSupplier,接口里的函数get()没有参数,那么在Student.activateStudent(Student::new)中调用的其实是Student()

4. 代码

package Lambda;

import java.util.Arrays;
import java.util.Comparator;

public class MethodReferenceClass {
    static class Student{
        int age;
        String name;
        boolean gender;

        Student(){
            this(0,"",true);
        }

        Student(int age, String name, boolean gender){
            this.age = age;
            this.name = name;
            this.gender = gender;
        }

        public String getName() {
            return name;
        }

        //根据学生名字的首字母排序
        static int compareByName(Student a, Student b){
            return a.getName().compareToIgnoreCase(b.getName());
        }

        int compareByNameInArbitraryInstance(Student a, Student b){
            return a.getName().compareToIgnoreCase(b.getName());
        }

        int compareByNameInInstance(Student a){
            return this.getName().compareToIgnoreCase(a.getName());
        }

        static void activateStudent(StudentSupplier studentSupplier){
            Student student = studentSupplier.get();
        }

        interface StudentSupplier{
            Student get();
        }

    }

    public static void main(String[] args) {
        Student s1 = new Student(12, "Abby", false);
        Student s2 = new Student(12, "Rocky", true);
        Student s3 = new Student(12, "Bobby", true);

        Student[] students = new Student[]{s1,s2,s3};

        //1. 通过新建一个比较器匿名类类实现Comparator接口实现
        Arrays.sort(students, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.getName().compareToIgnoreCase(o2.getName());
            }
        });

        //2. 通过使用比较器类的的lambda表达式
        Arrays.sort(students, (a, b) -> a.getName().compareToIgnoreCase(b.getName()));

        //3. 由于Student类中已经存在了这样一个方法,可以不再写比较的语句
        Arrays.sort(students, (a, b) -> Student.compareByName(a,b));

        //4. 此时可以看出,lambda表达式中只调用了一个方法,那么就可以使用方法引用的形式
        Arrays.sort(students, Student::compareByName);

        //5. 通过实例+成员方法实现方法引用
        Arrays.sort(students, students[0]::compareByNameInArbitraryInstance);

        //6. 通过类+成员方法实现引用
        Arrays.sort(students, Student::compareByNameInInstance);

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

推荐阅读更多精彩内容