我在研究Java 8源代码时,发现这部分代码非常令人惊讶:
// Defined in IntPipeline.java
@Override
public final OptionalInt reduce(IntBinaryOperator op) {
return evaluate(ReduceOps.makeInt(op));
}
@Override
public final OptionalInt max() {
return reduce(Math::max); // This is the gotcha line
}
// Defined in Math.java
public static int max(int a, int b) {
return (a >= b) ? a : b;
}
max是一个方法指针吗?一个正常的静态方法如何转换为IntBinaryOperator?
关于::方法引用的作用,前面的回答相当完整。总而言之,它提供了一种在不执行方法(或构造函数)的情况下引用方法(或构造函数)的方法,并且在计算时,它创建了提供目标类型上下文的函数接口实例。
下面是两个示例,在使用::方法引用和不使用::方法引用的情况下查找ArrayList中具有最大值的对象。解释见下面的评论。
不使用::
import java.util.*;
class MyClass {
private int val;
MyClass (int v) { val = v; }
int getVal() { return val; }
}
class ByVal implements Comparator<MyClass> {
// no need to create this class when using method reference
public int compare(MyClass source, MyClass ref) {
return source.getVal() - ref.getVal();
}
}
public class FindMaxInCol {
public static void main(String args[]) {
ArrayList<MyClass> myClassList = new ArrayList<MyClass>();
myClassList.add(new MyClass(1));
myClassList.add(new MyClass(0));
myClassList.add(new MyClass(3));
myClassList.add(new MyClass(6));
MyClass maxValObj = Collections.max(myClassList, new ByVal());
}
}
使用::
import java.util.*;
class MyClass {
private int val;
MyClass (int v) { val = v; }
int getVal() { return val; }
}
public class FindMaxInCol {
static int compareMyClass(MyClass source, MyClass ref) {
// This static method is compatible with the compare() method defined by Comparator.
// So there's no need to explicitly implement and create an instance of Comparator like the first example.
return source.getVal() - ref.getVal();
}
public static void main(String args[]) {
ArrayList<MyClass> myClassList = new ArrayList<MyClass>();
myClassList.add(new MyClass(1));
myClassList.add(new MyClass(0));
myClassList.add(new MyClass(3));
myClassList.add(new MyClass(6));
MyClass maxValObj = Collections.max(myClassList, FindMaxInCol::compareMyClass);
}
}
这是Java 8中的一个方法引用。Oracle文档在这里。
如文件所述……
方法引用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)。
通常,我们会使用Math调用reduce方法。Max (int, int)如下:
reduce(new IntBinaryOperator() {
int applyAsInt(int left, int right) {
return Math.max(left, right);
}
});
这需要大量的语法来调用Math.max。这就是lambda表达式发挥作用的地方。因为Java 8允许它以更短的方式做同样的事情:
reduce((int left, int right) -> Math.max(left, right));
这是如何工作的呢?java编译器“检测”到你想要实现一个接受两个int型并返回一个int型的方法。这等价于接口IntBinaryOperator的唯一方法的形式参数(要调用的方法reduce的参数)。所以编译器会帮你完成剩下的工作——它只是假设你想要实现IntBinaryOperator。
但作为数学。max(int, int)本身满足IntBinaryOperator的形式要求,它可以直接使用。因为Java 7没有任何允许方法本身作为参数传递的语法(你只能传递方法结果,但不能传递方法引用),所以Java 8引入了::语法来引用方法:
reduce(Math::max);
注意,这将由编译器解释,而不是由JVM在运行时解释!虽然它为所有三个代码段生成了不同的字节码,但它们在语义上是相等的,因此后两个可以被认为是上面IntBinaryOperator实现的简短(而且可能更有效)版本!
(参见Lambda表达式的翻译)
::操作符是在Java 8中引入的,用于方法引用。方法引用是只执行一个方法的lambda表达式的简写语法。下面是方法引用的一般语法:
Object :: methodName
我们知道可以使用lambda表达式,而不是使用匿名类。但有时,lambda表达式实际上只是对某些方法的调用,例如:
Consumer<String> c = s -> System.out.println(s);
为了使代码更清晰,你可以将lambda表达式转换为方法引用:
Consumer<String> c = System.out::println;