概述
Java 8 引入了新特性lambda表达式。Lambda表达式是一种不隶属于任何类的匿名方法。其可以使用在函数接口(functional interface)定义的变量。函数接口是一种只包含一个抽象方法(abstract method),同时可以包含多个默认方法(default method)和静态方法(static method)的接口。因此,lambda表达式也可以看做另一种形式的匿名类。
方法引用是一个与Lambda表达式相关的重要特性。可以同lambda一样,将方法引用传递给函数接口。方法引用的形式如:
Object::methodName
双引号::用来表示引用一个方法。
方法引用分类
有四种类型的方法引用。具体如下
| 类型 | 方法引用语法 | Lambda表达式语法 |
|---|---|---|
| 引用静态方法(Reference to a static method ) | ClassName::staticMethodName | (args) -> ClassName.staticMethodName(args) |
| 对已存在对象的实例方法的引用(Reference to an instance method of an existing object) | object::instanceMethodName | (args) -> object.instanceMethodName(args) |
| 引用特定类型的任意对象的实例方法(Reference to an instance method of an arbitrary object of a particular type) | ClassName::instanceMethodName | (arg0,rest) -> arg0.instanceMethodName(rest) Note: arg0 is of type ClassName |
| 引用构造器(Reference to a constructor) | ClassName::new | (args) -> new ClassName(args) |
其中,引用特定类型的任意对象的实例方法可能相对难以理解,下面本文将通过实例详细解读这四种方法引用类型。
引用静态方法
下述代码通过定义一个函数接口interface IntPredicate,该接口有一个抽象方法check()。check()方法接受一个类型为int参数,且返回值为类型boolean。IntPredicatesChecker类定义了两个静态方法。MethodRef类则有一个numCheck()方法,第一个参数为函数接口IntPredicate类型,第二个参数为int。 本实例通过numCheck的第一个参数分别接受lambda和静态方法引用来说明静态方法引用的使用。
public class MethodRef {
public MethodRef() {
}
// 该方法将函数接口作为第一个参数
public boolean numCheck(IntPredicate p, int n) {
return p.check(n);
}
public static void main(String[] args) {
MethodRef demo = new MethodRef();
int num = 11;
// 使用lambda 表达式检查number是否偶数
IntPredicate lb1 = number -> (number % 2) == 0;
boolean result = demo.numCheck(lb1, num);
System.out.println("Using lambda expression: " + num + " is even: " + result);
// IntPredicatesChecker 的isEven方法作为静态方法引用传递给numCheck()
result = demo.numCheck(IntPredicatesChecker::isEven, num);
System.out.println("Using static method reference: " + num + " is even: " + result);
// 使用lambda表达式检查number是否为正数
IntPredicate lb2 = number -> number > 0;
result = demo.numCheck(lb2, num);
System.out.println("Using lambda expression: " + num + " is positive: " + result);
// 使用IntPredicatesChecker的isPositive方法作为静态方法引用传递给numCheck()
result = demo.numCheck(IntPredicatesChecker::isPositive, num);
System.out.println("Using static method reference: " + num + " is positive: " + result);
}
}
@FunctionalInterface
interface IntPredicate {
boolean check(int i);
}
class IntPredicatesChecker {
// 一个静态方法 用来检查number n 是否为正数
public static boolean isPositive(int n) {
return n > 0;
}
//一个静态方法 用来检查 number n是否为偶数
public static boolean isEven(int n) {
return (n % 2) == 0;
}
}
输出如下
Using lambda expression: 11 is even: false
Using static method reference: 11 is even: false
Using lambda expression: 11 is positive: true
Using static method reference: 11 is positive: true
可以看到lambda表达式与静态方法引用的使用可以得到相同的结果。注意到 lambda表达式与静态方法的函数描述符相同,即参数为一个int类型,返回值为boolean类型。函数描述符相同则表明lambda与方法引用可以相互替换。
对已存在对象的实例方法的引用
这里同样使用IntPredicate函数接口。同时声明了一个类IntNumChecker,该类有一个方法isBigger(),isBigger()方法与IntPredicate的check()具有相同的函数描述符。*IntPredicate p = checker::isBigger;*表明对象实例checker的isBigger方法传递给函数接口IntPredicate。
public class MethodRef {
public static void main(String[] args) {
IntNumChecker checker = new IntNumChecker(10);
int numToCompare = 9;
IntPredicate p = checker::isBigger;
boolean result = p.check(numToCompare);
if (result) {
System.out.println(checker.getNum() + " is bigger than " + numToCompare);
} else {
System.out.println(checker.getNum() + " is smaller or equal than " + numToCompare);
}
}
}
@FunctionalInterface
interface IntPredicate {
boolean check(int i);
}
class IntNumChecker {
final private int num;
public IntNumChecker(int num) {
this.num = num;
}
public int getNum() {
return num;
}
// 检查num是否大于输入的n
boolean isBigger(int n) {
return num > n;
}
}
上述代码输出为
10 is bigger than 9
引用特定类型的任意对象的实例方法
这种类型是四种方法引用种最抽象的,很多文章都没有讲清楚。在这种实例中,可以使用类名替代对象名来引用方法。函数接口的第一个参数与调用的对象匹配,如本例的函数接口IntNumPredicate,其抽象方法的boolean check(IntNumChecker m, int n)的第一个参数即为IntNumChecker对象。这也是为什么该类型又被称为参数方法引用(parameter method reference)。
public class MethodRef {
public static void main(String[] args) {
IntNumChecker checker1 = new IntNumChecker(10);
int numToCompare = 9;
IntNumPredicate p = IntNumChecker::isBigger;
boolean result = p.check(checker1, 9);
if (result) {
System.out.println(checker1.getNum() + " is bigger than " + numToCompare);
} else {
System.out.println(checker1.getNum() + " is smaller " + numToCompare);
}
// IntNumChecker第二个对象checker2
IntNumChecker checker2 = new IntNumChecker(8);
result = p.check(checker2, 9);
if (result) {
System.out.println(checker2.getNum() + " is bigger than " + numToCompare);
} else {
System.out.println(checker2.getNum() + " is smaller " + numToCompare);
}
}
}
@FunctionalInterface
interface IntNumPredicate {
boolean check(IntNumChecker m, int n);
}
class IntNumChecker {
final private int num;
public IntNumChecker(int num) {
this.num = num;
}
// 检查num是否大于n
boolean isBigger(int n) {
return num > n;
}
public int getNum() {
return num;
}
}
输出为
10 is bigger than 9
8 is smaller 9
IntNumPredicate p可以使用在任意checker1和checker2对象上。
引用构造器
构造器引用类似于静态函数引用。函数接口IntSupplier的抽象方法*ObjIntCreation apply(int n)*与ObjIntCreation的构造器的函数描述符相同,故可以使用ObjIntCreation::new调用。
public class MethodRef {
public static void main(String[] args) {
// 使用函数接口 IntSupplier
int num = 10;
IntSupplier s1 = ObjIntCreation::new;
ObjIntCreation newObj1 = s1.apply(num);
System.out.println("new object has a instance value " + newObj1.getNum());
// 使用函数接口 IntObjectSupplier
IntObjectSupplier s2 = ObjIntCreation::new;
ObjIntCreation newObj = s2.apply(newObj1);
System.out.println("new object has a instance value " + newObj.getNum());
}
}
@FunctionalInterface
interface IntSupplier {
ObjIntCreation apply(int n);
}
@FunctionalInterface
interface IntObjectSupplier {
ObjIntCreation apply(ObjIntCreation obj);
}
class ObjIntCreation {
private int num;
public ObjIntCreation(int num) {
this.num = num;
}
public ObjIntCreation(ObjIntCreation n) {
this.num = n.num;
}
public int getNum() {
return num;
}
}
输出如下
new object has a instance value 10
new object has a instance value 10
方法引用优点
通过上述详细剖析方法引用的四种类型,既然方法引用可以与lambda表达式相互替换,那未为什么还要使用方法引用呢?以下将介绍方法引用相比于lambda表达式的优势在于:
1.代码可读性更好,因为具名
总结
本文详细介绍了方法引用的分类、使用方法以及优点。如发现任何问题,请留言交流。
参考
[1]Java Lambda: Method Reference
[2]Method References in Java
本文详述了Java 8中的方法引用,包括引用静态方法、已存在对象的实例方法、特定类型任意对象的实例方法及构造器。通过实例展示了它们的使用,并对比了方法引用与lambda表达式的差异,强调了方法引用在代码可读性上的优势。
961

被折叠的 条评论
为什么被折叠?



