方法参考

Java 8中的方法参考

介绍

到目前为止,添加到Java中最甜的语法糖肯定是Lambda Expressions

Java是一种冗长的语言,妨碍生产率和可读性。减少样板代码和重复性代码一直是Java开发人员的热门任务,并且通常追求简洁,可读,简洁的代码。

对于一些常见任务,Lambda Expressions消除了键入繁琐的样板代码的需要,它允许开发人员在不属于类的情况下调用它们并将它们像对象一样传递。

这些表达式在Java Streams API和Spring的WebFlux框架中主要用于创建反应性,动态应用程序。

Java 8中另一个真正有用的功能是方法引用,它使Lambda Expressions更加简洁明了,方法是:在使用Lambda Expression仅用于调用a时,使用方法名称调用(引用)方法。方法。

方法参考

方法引用实质上是缩短的Lambda表达式,用于调用方法。

它们包括两个部分:

Class::method;

常见的示例是打印出说结果,例如订阅发布者服务或Java Stream:

someCodeChain.subscribe(System.out::println);

让我们看一下命令式代码的示例,然后我们将通过Lambda表达式转向功能代码,最后通过方法引用进行缩短。

我们将做一个简单的类:

public class Employee {
    private int id;
    private String name;
    private int wage;
    private String position;

    // Constructor, getters and setters

    @Override
    public String toString() {
        return "Name: " + name + ", Wage: " + wage + ", Position: " + position;
    }

    public int compareTo(Employee employee) {
        if (this.wage <= employee.wage) {
            return 1;
        } else {
            return -1;
        }
    }
}

如果我们将该类形成一个集合,例如ArrayList,我们将无法使用实用程序方法对其进行排序,.sort()因为它没有实现Comparable接口。

我们可以做的是new Comparator在调用.sort()方法时为这些对象定义一个:

Employee emp1 = new Employee(1, "David", 1200, "Developer");
Employee emp2 = new Employee(2, "Tim", 1500, "Developer");
Employee emp3 = new Employee(3, "Martha", 1300, "Developer");

ArrayList<Employee> employeeList = new ArrayList<>();
employeeList.add(emp1);
employeeList.add(emp2);
employeeList.add(emp3);

Collections.sort(employeeList, new Comparator<Employee>() {
    public int compare(Employee emp1, Employee emp2) {
        return emp1.compareTo(emp2);
    }
});

System.out.println(employeeList);

运行此代码将产生:

[Name: Tim, Wage: 1500, Position: Developer, Name: Martha, Wage: 1300, Position: Developer, Name: David, Wage: 1200, Position: Developer]

在此,匿名类(Comparator)正在定义比较条件。通过使用Lambda表达式,我们可以使其更简单,更短:

Collections.sort(employeeList, (e1, e2) -> e1.compareTo(e2));

运行这段代码将产生:

[Name: Tim, Wage: 1500, Position: Developer, Name: Martha, Wage: 1300, Position: Developer, Name: David, Wage: 1200, Position: Developer]

再说一次,由于我们对Lambda表达式所做的全部工作都称为一个方法,因此我们只能引用该方法:

Collections.sort(employeeList, Employee::compareTo);

这还将产生:

[Name: Tim, Wage: 1500, Position: Developer, Name: Martha, Wage: 1300, Position: Developer, Name: David, Wage: 1200, Position: Developer]

方法参考类型

方法引用可以在两种不同的情况下使用:

  • 静态方法: Class::staticMethodName
  • 特定对象的实例方法: object::instanceMethodName
  • 仲裁对象的实例方法: Class::methodName
  • 构造函数参考: Class::new

让我们通过一些简单的示例来研究所有这些类型。

静态方法参考

您可以static通过简单地用方法名称调用其包含类来引用类的任何方法。

让我们用static方法定义一个类,然后从另一个类中引用它:

public class ClassA {
    public static void raiseToThePowerOfTwo(double num) {
        double result = Math.pow(num, 2);
        System.out.println(result);
    }
}

现在,从另一个类中,让我们使用static实用程序方法:

public class ClassB {
    public static void main(String[] args) {
        List<Double> integerList = new ArrayList<>();
        integerList.add(new Double(5));
        integerList.add(new Double(2));
        integerList.add(new Double(6));
        integerList.add(new Double(1));
        integerList.add(new Double(8));
        integerList.add(new Double(9));

        integerList.forEach(ClassA::raiseToThePowerOfTwo);
    }
}

运行这段代码将产生:

25.0
4.0
36.0
1.0
64.0
81.0

有许多Java类提供static可以在此处使用的实用程序方法。在我们的示例中,我们使用了一种自定义方法,尽管在这种情况下不是一个非常有用的方法。

特定对象的实例方法

通过使用对象的引用变量引用该方法,可以从特定的实例化对象调用方法。

订阅我们的新闻
在收件箱中获取临时教程,指南和作业。从来没有垃圾邮件。随时退订。

订阅电子报
订阅

这通常通过自定义比较器进行说明。我们将使用Employee之前的相同类和相同的列表来突出显示两者之间的区别:

public class Employee {
    private int id;
    private String name;
    private int wage;
    private String position;

    // Constructor, getters and setters

    @Override
    public String toString() {
        return "Name: " + name + ", Wage: " + wage + ", Position: " + position;
    }

    public int compareTo(Employee employee) {
        if (this.wage <= employee.wage) {
            return 1;
        } else {
            return -1;
        }
    }
}

现在,让我们定义一个CustomComparator

public class CustomComparator {
    public int compareEntities(Employee emp1, Employee emp2) {
        return emp1.compareTo(emp2);
    }
}

最后,让我们填充一个列表并对其进行排序:

Employee emp1 = new Employee(1, "David", 1200, "Developer");
Employee emp2 = new Employee(2, "Tim", 1500, "Developer");
Employee emp3 = new Employee(3, "Martha", 1300, "Developer");

ArrayList<Employee> employeeList = new ArrayList<>();
employeeList.add(emp1);
employeeList.add(emp2);
employeeList.add(emp3);

// Initializing our CustomComparator
CustomComparator customComparator = new CustomComparator();

// Instead of making a call to an arbitrary Employee
// we're now providing an instance and its method
Collections.sort(employeeList, customComparator::compareEntities);

System.out.println(employeeList);

运行此代码还将产生:

[Name: Tim, Wage: 1500, Position: Developer, Name: Martha, Wage: 1300, Position: Developer, Name: David, Wage: 1200, Position: Developer]

主要区别在于,通过添加了另一层,CustomComparator我们可以添加更多功能进行比较并将其从类本身中删除。诸如此类的类Employee不应承受复杂的比较逻辑的负担,这将导致更清晰,更易读的代码。

另一方面,有时我们不希望定义自定义比较器,而引入一个比较器简直太麻烦了。在这种情况下,我们将从特定类型的任意对象中调用方法,如下一节所示。

任意对象的实例方法

当我们将命令式方法通过Lambda Expressions分解为功能性方法时,该示例已在本文开头显示。

不过,从良好的角度来看,由于这种方法的使用频率很高,因此让我们来看另一个示例:

List<Integer> integerList = new ArrayList<>();
integerList.add(new Integer(5));
integerList.add(new Integer(2));
integerList.add(new Integer(6));
integerList.add(new Integer(1));
integerList.add(new Integer(8));
integerList.add(new Integer(9));

// Referencing the non-static compareTo method from the Integer class
Collections.sort(integerList, Integer::compareTo);

// Referencing static method
integerList.forEach(System.out::print);

运行这段代码将产生:

125689

虽然这可能看起来像它一样的静态方法的调用,它不是。这等效于调用Lambda表达式:

Collections.sort(integerList, (Integer a, Integer b) -> a.compareTo(b));

在这里,区别更加明显。如果我们要调用一个static方法,它将看起来像:

Collections.sort(integerList, (Integer a, Integer b) -> SomeClass.compare(a, b));

引用构造函数

您可以像引用static方法一样引用类的构造函数。

您可以使用对构造函数的引用,而不是经典的类实例化:

// Classic instantiation
Employee employee = new Employee();

// Constructor reference
Employee employee2 = Employe::new;

根据上下文,如果存在多个构造函数,则在引用时将使用足够的构造函数:

Stream<Employee> stream = names.stream().map(Employee::new);

由于名称流,如果存在Employee(String name)构造函数,则将使用它。

可以使用构造函数引用的另一种方式是,当您想要将流映射到数组中时,同时保留特定的类型。如果要简单地映射它,然后调用toArray(),则会得到Objects数组,而不是您的特定类型。

如果我们尝试过,请说:

Employee[] employeeArray = employeeList.toArray();

当然,由于.toArray()返回Objects数组,因此会遇到编译器错误。投射它也无济于事:

Employee[] employeeArray = (Employee[]) employeeList.toArray();

但是这次,它将是运行时异常- ClassCastException

我们可以通过以下方法避免这种情况:

// Making a list of employees
List<String> employeeList = Arrays.asList("David", "Scott");

// Mapping a list to Employee objects and returning them as an array
Employee[] employeeArray = employeeList.stream().map(Employee::new).toArray(Employee[]::new);

// Iterating through the array and printing information
for (int i = 0; i < employeeArray.length; i++) {
    System.out.println(employeeArray[i].toString());
}

这样,我们得到输出:

Name: David, Wage: 0, Position: null
Name: Scott, Wage: 0, Position: null

结论

方法引用是Lambda表达式的一种,用于在调用中简单地引用方法。使用它们,编写代码可以更加简洁和可读。

Lambda Expressions向Java开发人员介绍了一种更具功能性的编程方法,该方法允许他们避免为简单操作编写冗长的代码。

发布于 2021-01-09 16:16

转自 何鹏

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

推荐阅读更多精彩内容