我在研究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?


当前回答

在运行时,它们的行为完全相同。字节码可能不相同(对于上面的情况,它生成相同的字节码(编译上面的代码并检查javaap -c;))。

在运行时,它们的行为完全相同。方法(math::max)生成相同的数学结果(编译上面的代码并检查javap -c;))。

其他回答

::操作符是在Java 8中引入的,用于方法引用。方法引用是只执行一个方法的lambda表达式的简写语法。下面是方法引用的一般语法:

Object :: methodName

我们知道可以使用lambda表达式,而不是使用匿名类。但有时,lambda表达式实际上只是对某些方法的调用,例如:

Consumer<String> c = s -> System.out.println(s);

为了使代码更清晰,你可以将lambda表达式转换为方法引用:

Consumer<String> c = System.out::println;

这是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)。

我发现这个来源非常有趣。

事实上,是lambda变成了双冒号。双冒号可读性更好。

我们遵循以下步骤:

步骤1

// We create a comparator of two persons
Comparator c = (Person p1, Person p2) -> p1.getAge().compareTo(p2.getAge());

步骤2

// We use the interference
Comparator c = (p1, p2) -> p1.getAge().compareTo(p2.getAge());

步骤3

// The magic using method reference
Comparator c = Comparator.comparing(Person::getAge);

关于::方法引用的作用,前面的回答相当完整。总而言之,它提供了一种在不执行方法(或构造函数)的情况下引用方法(或构造函数)的方法,并且在计算时,它创建了提供目标类型上下文的函数接口实例。

下面是两个示例,在使用::方法引用和不使用::方法引用的情况下查找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);
    }
}

在运行时,它们的行为完全相同。字节码可能不相同(对于上面的情况,它生成相同的字节码(编译上面的代码并检查javaap -c;))。

在运行时,它们的行为完全相同。方法(math::max)生成相同的数学结果(编译上面的代码并检查javap -c;))。