Java 8 中的 ::(双冒号)运算符

新手上路,请多包涵

我正在探索 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

原文由 Narendra Pathai 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 692
2 个回答

通常,人们会使用 Math.max(int, int) 调用 reduce 方法,如下所示:

 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

但由于 Math.max(int, int) 本身满足 IntBinaryOperator 的形式要求,可以直接使用。因为 Java 7 没有任何允许将方法本身作为参数传递的语法(您只能传递方法结果,而不能传递方法引用),Java 8 中引入了 :: 语法来引用方法:

 reduce(Math::max);

请注意,这将由编译器解释,而不是在运行时由 JVM 解释!尽管它为所有三个代码片段生成不同的字节码,但它们在语义上是相同的,所以最后两个可以被认为是上面 IntBinaryOperator 实现的简短(并且可能更有效)版本!

(另请参阅 Lambda 表达式的翻译

原文由 isnot2bad 发布,翻译遵循 CC BY-SA 4.0 许可协议

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

简短说明

下面是引用静态方法的示例:

 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 可以像对象引用一样传递并在需要时触发。事实上,它可以像 static 一样容易地用作对对象“正常”方法的引用。例如:

 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);

Function 以上是 功能接口。要完全理解 :: ,理解功能接口也很重要。简而言之, 函数式接口 是只有一个抽象方法的接口。

功能接口的示例包括 RunnableCallableActionListener

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


:: 之所以很棒是 因为

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

例如,而不是编写 lambda 主体

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

你可以简单地做

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

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

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

以下是非法的:

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

square 需要一个参数并返回一个 doubleSupplier 中的 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;

方法引用可以有不同的风格,但从根本上说它们都意味着同一件事,可以简单地可视化为 lambda:

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

如需进一步参考,请参阅 State of the Lambda

原文由 Jatin 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏