Java8————方法引用

译者注:本篇博客翻译自Oracle官方教程《Method References》。作为Java 8 新特性Lambda表达式的引申概念,博主依然采用官方文档的方式来学习这一重要的概念。希望对各位同道有所帮助。

方法引用

使用Lambda表达式创建匿名方法。但是,有时候Lambda表达式什么都没做,仅仅是调用了一个已经存在的方法。这种情况下,引用已存在方法的方法名通常是更清晰的。方法引用允许你这么做,它是一种简洁的、可读性强的有名方法的Lambda表达式。

再次思考我们在Lambda 表达式部分(即上一篇翻译《Java8————Lambda表达式(二)》)中讨论的Person类:

public class Person {

    public enum Sex {
        MALE, FEMALE
    }

    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;

    public int getAge() {
        // ...
    }
    
    public Calendar getBirthday() {
        return birthday;
    }    

    public static int compareByAge(Person a, Person b) {
        return a.birthday.compareTo(b.birthday);
    }}

假设你的应用中的成员全都包含在一个数组中,并且你希望把他们以年龄进行排序。你可以使用下面的代码:

Person[] rosterAsArray = roster.toArray(new Person[roster.size()]);

class PersonAgeComparator implements Comparator<Person> {
    public int compare(Person a, Person b) {
        return a.getBirthday().compareTo(b.getBirthday());
    }
}
        
Arrays.sort(rosterAsArray, new PersonAgeComparator());

调用的sort方法的方法签名(译者注:方法签名是包含方法名、参数列表等在内的方法信息)如下:

static <T> void sort(T[] a, Comparator<? super T> c)

注意,Comparator接口是一个函数接口。因此,你可以使用Lambda表达式而不是去定义并随后创建一个实现了Comparator的类的实例:

Arrays.sort(rosterAsArray,
    (Person a, Person b) -> {
        return a.getBirthday().compareTo(b.getBirthday());
    }
);

但是,比较两个Person对象生日的方法已经存在于Person.compareByAge方法当中。你可以Lambda表达式的body中调用:

Arrays.sort(rosterAsArray,
    (a, b) -> Person.compareByAge(a, b)
);

因为这个Lambda表达式调用一个存在的方法,因此,你可以使用方法引用来代替Lambda表达式:

Arrays.sort(rosterAsArray, Person::compareByAge);

方法引用 Person::compareByAge语义上与Lambda表达式(a, b) -> Person.compareByAge(a, b) 是一样的。都具备以下特性:

1、它的参数列表:(Person , Person)从Comparator<Person>.compare复制过来。

2、它的主体调用方法Person.compareByAge()

方法引用的种类

有如下四种方法引用:

种类 距离
Reference to a static method ContainingClass::staticMethodName
Reference to an instance method of a particular object containingObject::instanceMethodName
Reference to an instance method of an arbitrary object of a particular type ContainingType::methodName
Reference to a constructor ClassName::new
  • 静态方法引用

方法引用如:Person::compareByAge 就是对静态方法的引用。

  • 特定对象的实例方法引用

下面的例子就是对特定对象的实例方法引用:

class ComparisonProvider {
    public int compareByName(Person a, Person b) {
        return a.getName().compareTo(b.getName());
    }
        
    public int compareByAge(Person a, Person b) {
        return a.getBirthday().compareTo(b.getBirthday());
    }
}
ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);

方法引用:myComparisonProvider::compareByName,调用了myComparisonProvider对象的方法compareByName。JRE会推断出方法的参数类型,这个例子中是(Person, Person)。

  • 特定类型的任意对象的实例方法引用

下面的例子是特定类型的任意对象的实例方法引用:

String[] stringArray = { "Barbara", "James", "Mary", "John",
    "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);

与Lambda表达式等价的方法引用 String::compareToIgnoreCase 有一个正式的参数列表:(String a, String b), a 和b 都是用来更好的描述这个例子的任意的名称。这个方法引用会调用方法:a.compareToIgnoreCase(b)。

  • 构造器的方法引用

你可以像使用静态方法引用的方式那样使用new关键字来引用构造器。下面的方法将元素从一个集合中拷贝到另一个集合中。

public static <T, SOURCE extends Collection<T>, DEST extends Collection<T>>
    DEST transferElements(
        SOURCE sourceCollection,
        Supplier<DEST> collectionFactory) {
        
        DEST result = collectionFactory.get();
        for (T t : sourceCollection) {
            result.add(t);
        }
        return result;
}

函数接口Supplier包含一个没有参数并且返回一个对象的方法get()。因此,你可以像下面这样,通过Lambda表达式调用transferElements()方法:

Set<Person> rosterSetLambda =
    transferElements(roster, () -> { return new HashSet<>(); });

你可以像下面这样使用构造器引用来取代Lambda表达式:

Set<Person> rosterSet = transferElements(roster, HashSet::new);

Java编译器会推断出你希望创建一个包含元素类型为Person的HashSet集合。你可以像下面这样来指定,二选一。

Set<Person> rosterSet = transferElements(roster, HashSet<Person>::new);

猜你喜欢

转载自blog.csdn.net/u014745069/article/details/82940729