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


当前回答

::是Java 8中包含的一个新操作符,用于引用现有类的方法。可以引用类的静态方法和非静态方法。

对于引用静态方法,语法为:

ClassName :: methodName 

对于引用非静态方法,语法为

objRef :: methodName

And

ClassName :: methodName

引用方法的唯一前提是该方法存在于功能接口中,该接口必须与方法引用兼容。

计算方法引用时,将创建功能接口的实例。

这是在http://www.speakingcs.com/2014/08/method-references-in-java-8.html上找到的

其他回答

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

事实上,是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);

::被称为方法引用。它基本上是对单个方法的引用。也就是说,它通过名称引用一个现有的方法。

简短说明:

下面是一个引用静态方法的例子:

class Hey {
     public static double square(double num){
        return Math.pow(num, 2);
    }
}

Function<Double, Double> square = Hey::square;
double ans = square.apply(23d);

Square可以像对象引用一样传递,并在需要时触发。事实上,它可以很容易地作为对象的“正常”方法的引用使用,就像静态方法一样。例如:

class Hey {
    public double square(double num) {
        return Math.pow(num, 2);
    }
}

Hey hey = new Hey();
Function<Double, Double> square = hey::square;
double ans = square.apply(23d);

以上是一个功能接口。为了充分理解::,理解功能接口也很重要。简单地说,函数式接口就是只有一个抽象方法的接口。

功能性接口的例子包括Runnable、Callable和ActionListener。

上面的函数是一个函数接口,只有一个方法:apply。它需要一个参数并产生一个结果。


::s很棒的原因是:

方法引用是与lambda表达式(…)具有相同处理方式的表达式,但它们不是提供方法体,而是通过名称引用现有方法。

例如,而不是写lambda体

Function<Double, Double> square = (Double x) -> x * x;

你可以简单地

Function<Double, Double> square = Hey::square;

在运行时,这两个平方方法的行为完全相同。字节码可以是相同的,也可以不是相同的(尽管,对于上面的情况,生成了相同的字节码;编译上面的代码并用javap -c检查)。

唯一需要满足的主要标准是:您提供的方法应该与用作对象引用的函数接口的方法具有类似的签名。

以下内容是非法的:

Supplier<Boolean> p = Hey::square; // illegal

Square需要参数并返回double。Supplier中的get方法返回一个值,但它不接受参数。因此,这会导致一个错误。

方法引用是指功能接口的方法。(如前所述,函数接口只能有一个方法。)

再举一些例子:Consumer中的accept方法接受一个输入,但它不返回任何东西。

Consumer<Integer> b1 = System::exit;   // void exit(int status)
Consumer<String[]> b2 = Arrays::sort;  // void sort(Object[] a)
Consumer<String> b3 = MyProgram::main; // void main(String... args)

class Hey {
    public double getRandom() {
        return Math.random();
    }
}

Callable<Double> call = hey::getRandom;
Supplier<Double> call2 = hey::getRandom;
DoubleSupplier sup = hey::getRandom;
// Supplier is functional interface that takes no argument and gives a result

上面,getRandom不接受任何参数并返回一个double。因此,任何满足以下条件的功能接口都可以使用:不接受参数并返回double。

另一个例子:

Set<String> set = new HashSet<>();
set.addAll(Arrays.asList("leo","bale","hanks"));
Predicate<String> pred = set::contains;
boolean exists = pred.test("leo");

对于参数化类型:

class Param<T> {
    T elem;
    public T get() {
        return elem;
    }

    public void set(T elem) {
        this.elem = elem;
    }

    public static <E> E returnSame(E elem) {
        return elem;
    }
}

Supplier<Param<Integer>> obj = Param<Integer>::new;
Param<Integer> param = obj.get();
Consumer<Integer> c = param::set;
Supplier<Integer> s = param::get;

Function<String, String> func = Param::<String>returnSame;

方法引用可以有不同的风格,但基本上它们都意味着相同的事情,可以简单地可视化为lambdas:

一个静态方法(ClassName::methName) 特定对象的实例方法(instanceRef::methName) 特定对象的超方法(super::methName) 特定类型的任意对象的实例方法(ClassName::methName) 类构造函数引用(ClassName::new) 数组构造函数引用(TypeName[]::new)

有关进一步参考,请参阅Lambda的状态。

是的,那是真的。操作符用于方法引用。因此,可以使用它从类中提取静态方法,也可以从对象中提取方法。相同的操作符甚至可以用于构造函数。下面的代码示例演示了这里提到的所有情况。

Oracle的官方文档可以在这里找到。

在本文中,您可以更好地概述JDK 8的更改。在方法/构造函数引用部分还提供了一个代码示例:

interface ConstructorReference {
    T constructor();
}

interface  MethodReference {
   void anotherMethod(String input);
}

public class ConstructorClass {
    String value;

   public ConstructorClass() {
       value = "default";
   }

   public static void method(String input) {
      System.out.println(input);
   }

   public void nextMethod(String input) {
       // operations
   }

   public static void main(String... args) {
       // constructor reference
       ConstructorReference reference = ConstructorClass::new;
       ConstructorClass cc = reference.constructor();

       // static method reference
       MethodReference mr = cc::method;

       // object method reference
       MethodReference mr2 = cc::nextMethod;

       System.out.println(cc.value);
   }
}

在旧的Java版本中,你可以使用:而不是"::"或lambd:

public interface Action {
    void execute();
}

public class ActionImpl implements Action {

    @Override
    public void execute() {
        System.out.println("execute with ActionImpl");
    }

}

public static void main(String[] args) {
    Action action = new Action() {
        @Override
        public void execute() {
            System.out.println("execute with anonymous class");
        }
    };
    action.execute();

    //or

    Action actionImpl = new ActionImpl();
    actionImpl.execute();
}

或者传递给方法:

public static void doSomething(Action action) {
    action.execute();
}

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