方法引用

Lambda表达式可以用来创建匿名方法。有时候,一个Lambda表达式只是调用了一下已经存在的方法, 其它的啥也没干。在这种情况下,如果能够直接引用方法的名字会使代码看起来很清爽简洁。方法引用就能满足这一点,它们就是紧凑、易读的用Lambda表达式。

让我们再来看一看之前在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);
    }}

假定你的社交网站的成员都存储在一个数组里, 你想根据年龄把成员排序。你可以用下面的代码 (如果要查看更多详细,可以参看这里 MethodReferencesTest

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表达式, 而没有必要创建一个实现接口的类再实例化它。

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

然而,你仔细观察的话会发现,其实这个比较出生日期的方法已经存在了,就是Person.compareByAge, 因此你可以直接调用这个方法,而不用在Lambda表达式主体里再实现一遍。

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) 在语义上完全一样的, 它们都有下列特征:

  • 它们的参数都是来自于Comparator<Person>.compare,也就是Person, Person)
  • 它们的主体都会调用Person.compareByAge.

方法引用的种类

总共有4种方法引用:

类别示例
引用静态方法ContainingClass::staticMethodName
引用一个特定对象的实例方法containingObject::instanceMethodName
引用一个特定类型的任意对象的实例方法ContainingType::methodName
引用构造方法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 会调用compareByName, 这是对象myComparisonProvider的一部分。JRE会推断方法的类型参数,在这种情况下就是(Person, Person).

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

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

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

对于方法引用String::compareToIgnoreCase, 与之等同的Lambda表达式,会有参数列表:(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, 没有参数,它返回一个对象。相应的,你可以在调用 方法transferElements的时候,使用Lambda表达式:

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

如果用构造方法引用替代Lambda表达式,就是如下:

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

Java编译器会推断你想创建一个Haset的集合, 并且用来存储Person元素。当然,你还可以用下面的方式表达:

Set<Person> rosterSet = transferElements(roster, HashSet<Person>::new);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值