Java8的::双冒号运算符的作用

一、概述

我正在研究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;
}

Math::max类似方法指针的东西吗?普通static方法如何转换为IntBinaryOperator

二、详解

::称为方法引用。它基本上是对单个方法的引用。它通过名称引用现有方法。

简短说明
以下是对静态方法的引用示例:

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

Function 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 square = hey::square;
double ans = square.apply(23d);

Function是一个功能接口要完全理解::,理解功能接口也很重要。显而易见,功能接口是仅具有一个抽象方法的接口。

功能接口的例子包括RunnableCallable,和ActionListener

Function是仅有一个方法的功能接口:apply它接受一个参数并产生结果。


为什么使用::操作符

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

例如,如果不编写lambda正文

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

你可以做

Function square = Hey::square;

在运行时,这两种square方法的行为完全相同。字节码可以相同也可以不相同(不过,对于上述情况,将生成相同的字节码;请编译以上内容并使用进行检查javap -c)。

要满足的唯一主要标准是:您提供的方法应该与用作对象引用的功能接口的方法具有相似的签名

以下是非法的:

Supplier p = Hey::square; // illegal

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

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

还有更多示例:Consumer中的accept方法接受输入,但不返回任何内容。

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

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

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

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

另一个例子:

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

如果是参数化类型

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

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

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

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

Function func = Param::returnSame;

方法引用可以具有不同的样式,但是从根本上讲,它们都表示同一件事,并且可以简单地可视化为lambda:

  1. 静态方法(ClassName::methName
  2. 特定对象(instanceRef::methName的实例方法
  3. 特定对象的超级方法(super::methName
  4. 特定类型(ClassName::methName的任意对象的实例方法
  5. 类构造函数参考(ClassName::new
  6. 数组构造函数参考(TypeName[]::new

有关更多参考,请参见http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html。

如若转载,请注明出处:https://www.javaidea.cn/article/8268.html

(0)
上一篇 2022年2月7日 上午10:03
下一篇 2022年2月7日 上午10:03

相关推荐

发表评论

您的电子邮箱地址不会被公开。