前言
上一文:Android 使用Java8新特性之Lambda expression (附命令者模式简化)
说过lambda表达式,在android studio中的环境配置及应用。本文讲下Java8新特性之”方法引用”。
“方法引用”,它其实可以看成lambda表达式的一种简写形式。
再回顾一下lambda表达式的应用场景:简化仅含单一抽象方法接口的调用
方法引用的4种形式
方法引用的符号形式,形如,
[className | class-instance]::[static-method | instance-method | construct-method]
即,以类名或类的实例对象为前缀,中间以两个冒号(::)连接,后跟静态方法 或 实例方法 或 构造方法
分类:
1. 静态方法引用
2. 实例方法引用
3. 类实例方法引用
4. 构造方法引用
静态方法引用
类MethodReferenceActivity下有一静态方法:
static int parz(String str) {//被lambda表达式或"方法引用"调用
return Integer.parseInt(str);
}
如果直接调用 MethodReferenceActivity::parz当然是不行的,因为”方法引用”是用来替换lambda表达式的,所以也将指代一个仅含单一抽象方法的接口。
再有一个接口:
public interface ToIntFunc<T> {//注:原生jdk中就有的方法,安卓中没有
int applyAsInt(T value);
}
再有一个使用ToIntFunc接口的静态方法:
public static int parse(ToIntFunc<String> f, String num) {
return f.applyAsInt(num);
}
调用parse()时使用lambda表达式:
int parseValue = parse((value -> parz(value)), num);
调用parse()时使用”方法引用”:
int parseValue = parse(MethodReferenceActivity::parz, num);
实例方法引用
将上面的方法parse方法声明中的static 去掉:
public int parse(ToIntFunc<String> f, String num) {
return f.applyAsInt(num);
}
调用parse()时使用”方法引用”:
int parseValue = parse(this::parz, num); //this指当前类的实例对象
类实例方法引用
需要在外部调用方法中,声明参数
如有一个内部类Person:
static class Person {
public Person() {
}
public Person(String name) {
this.name = name;
}
String name;
Long birthday;
public Long getBirthday() {
return birthday;
}
public String getName() {
return name;
}
public boolean compareByName(Person b) {
int result = this.name.compareToIgnoreCase(b.name);
if (result == 1) {
return true;
} else {
return false;
}
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", birthday=" + birthday +
'}';
}
public void printInfo() {
System.out.println(toString());
}
}
有一个接口Compare:
public interface Compare<T> {
boolean compareTo(T v1, T v2);
}
外部的使用接口Compare的调用方法isLarge:
public boolean isLarge(Compare<Person> c, Person p1, Person p2) {
return c.compareTo(p1, p2);
}
使用lambda表达式和类实例方法引用:
Person p1 = new Person("stone");
Person p2 = new Person("sun");
boolean result = isLarge((v1, v2) -> v1.compareByName(v2), p1, p2);
System.out.println("p1 排序在 p2 后: " + result);
result = isLarge(Person::compareByName, p1, p2);
System.out.println("p1 排序在 p2 后: " + result);
这里isLarge方法,除接口外,还要求传递两个Person的实例对象。这两个对象,被Compare接口中的compareTo方法使用。
不需要在外部调用方法中,声明参数
再来看如下一组代码:
Integer[] ary = {2, 8, 1, 5, 6, 3, 4};
Arrays.sort(ary, (o1, o2) -> o1.compareTo(o2));
Arrays.sort(ary, Integer::compareTo);
这是调用数组工具类Arrays.sort()来进行排序。
在【Arrays.sort(ary, Integer::compareTo);】中,”类实例方法引用”实际指代java.util.Comparator接口,接口中的静态方法为:int compare(T o1, T o2);而这里并没有传递o1、o2参数。o1,o2的实例化,实际上是在sort方法内部完成的。 就好比如下示例,
/**内部实例化*/
public boolean isLarge(Compare<Person> c) {
Person p1 = new Person("stone");
Person p2 = new Person("austin");
return c.compareTo(p1, p2);
}
使用lambda和类实例方法引用:
isLarge((v1, v2) -> v1.compareByName(v2));//lambda expression
isLarge(Person::compareByName);//method reference
构造方法引用
现有下面的代码:
static class Person {
String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
}
public interface GetType<T> {
T get(String value);
}
public interface GetType2<T> {
T get();
}
public Person getPerson(String name, GetType<Person> data) {
return data.get(name);
}
public Person getPerson(GetType2<Person> data) {
return data.get();
}
使用lambda和构造方法引用:
getPerson("li si", value -> new Person(value));//lambda
//会调用new Person(String name) 指代GetType
getPerson("zhang san", Person::new);
getPerson(() -> new Person());//lambda
getPerson(Person::new);//会调用new Person() 指代GetType2
总结
不管是lambda表达式,还是”方法引用”,它们都指代一个仅含单一抽象方法接口的匿名内部类实例。使用”方法引用”后,一看表达式,就能猜出方法引用的类别。同时配合AS,看调用方法的接口。
“方法引用”表达式的返回值与接口中方法的返回值一致。”方法引用”表达式所调用方法的参数,可以通过外部调用方法来传值。如果有参数,而没传值,可能在外部方法内部进行了初始化,如”类实例方法引用”第二个示例。
lambda表达式,是可以有一个方法体的。现在有了”方法引用”,就不用看起来那么”混乱”了。比如:
button.setOnTouchListener((view, event)-> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
//do sth.
return true;
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
//do sth.
return true;
}
return super.onTouchEvent(event);
});
只需要声明一个方法:
private boolean handleEvent(View v, MotionEvent event) {
//do sth.
return true;
}
如下使用”方法引用”调用:
button.setOnTouchListener(this::handleEvent);