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

2022-09-08 11:34:35 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

本文翻译自::: (double colon) operator in Java 8

I was exploring the Java 8 source and found this particular part of code very surprising: 我正在探索Java 8源代码,发现代码的这一特殊部分非常令人惊讶:

代码语言:javascript复制
//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;
}

Is Math::max something like a method pointer? Math::max是否类似于方法指针? How does a normal static method get converted to IntBinaryOperator ? 普通的static方法如何转换为IntBinaryOperator

#1楼

参考:https://stackoom.com/question/1LvHf/Java-中的-双冒号-运算符

#2楼

Yes, that is true. 是的,那是真的。 The :: operator is used for method referencing. ::运算符用于方法引用。 So, one can extract static methods from classes by using it or methods from objects. 因此,可以通过使用静态方法或对象的方法来提取静态方法。 The same operator can be used even for constructors. 即使对于构造函数,也可以使用相同的运算符。 All cases mentioned here are exemplified in the code sample below. 此处提到的所有情况在下面的代码示例中得到了示例。

The official documentation from Oracle can be found here

You can have a better overview of the JDK 8 changes in this article. 您可以在JDK 8的变化,更好地观察这个文章。 In the Method/Constructor referencing section a code example is also provided: 在“ 方法/构造函数引用”部分中,还提供了代码示例:

代码语言:javascript复制
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);
   }
}

#3楼

This is a method reference in Java 8. The oracle documentation is here . 这是Java 8中的方法参考 。

As stated in the documentation… 如文档中所述…

The method reference Person::compareByAge is a reference to a static method. 方法引用Person :: compareByAge是对静态方法的引用。 The following is an example of a reference to an instance method of a particular object: 以下是对特定对象的实例方法的引用示例:

代码语言:javascript复制
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); 

The method reference myComparisonProvider::compareByName invokes the method compareByName that is part of the object myComparisonProvider. 方法引用myComparisonProvider :: compareByName调用方法compareByName,它是对象myComparisonProvider的一部分。 The JRE infers the method type arguments, which in this case are (Person, Person). JRE推断方法类型参数,在这种情况下为(Person,Person)。


#4楼

Usually, one would call the reduce method using Math.max(int, int) as follows: 通常,可以使用Math.max(int, int)调用reduce方法Math.max(int, int)如下所示:

代码语言:javascript复制
reduce(new IntBinaryOperator() {
    int applyAsInt(int left, int right) {
        return Math.max(left, right);
    }
});

That requires a lot of syntax for just calling Math.max . 仅调用Math.max需要大量语法。 That’s where lambda expressions come into play. 那就是lambda表达式起作用的地方。 Since Java 8 it is allowed to do the same thing in a much shorter way: 从Java 8开始,允许以更短的方式执行相同的操作:

代码语言:javascript复制
reduce((int left, int right) -> Math.max(left, right));

How does this work? 这是如何运作的? The java compiler “detects”, that you want to implement a method that accepts two int s and returns one int . Java编译器“检测”您要实现一个接受两个int并返回一个int This is equivalent to the formal parameters of the one and only method of interface IntBinaryOperator (the parameter of method reduce you want to call). 这等效于接口IntBinaryOperator唯一方法的形式参数(方法参数reduce您要调用)。 So the compiler does the rest for you – it just assumes you want to implement IntBinaryOperator . 因此,编译器会为您完成其余工作-只是假设您要实现IntBinaryOperator

But as Math.max(int, int) itself fulfills the formal requirements of IntBinaryOperator , it can be used directly. 但是由于Math.max(int, int)本身满足IntBinaryOperator的形式要求,因此可以直接使用它。 Because Java 7 does not have any syntax that allows a method itself to be passed as an argument (you can only pass method results, but never method references), the :: syntax was introduced in Java 8 to reference methods: 由于Java 7没有任何语法允许将方法本身作为参数传递(您只能传递方法结果,而不能传递方法引用),因此Java 8中引入了::语法来引用方法:

代码语言:javascript复制
reduce(Math::max);

Note that this will be interpreted by the compiler, not by the JVM at runtime! 注意,这将由编译器而不是运行时的JVM解释! Although it produces different bytecodes for all three code snippets, they are semantically equal, so the last two can be considered to be short (and probably more efficient) versions of the IntBinaryOperator implementation above! 尽管它为所有三个代码段生成了不同的字节码,但它们在语义上是相等的,因此后两个可以视为上述IntBinaryOperator实现的IntBinaryOperator版本(并且可能效率更高)!

(See also Translation of Lambda Expressions ) (另请参见Lambda表达式的翻译


#5楼

:: is called Method Reference. ::称为方法参考。 It is basically a reference to a single method. 它基本上是对单个方法的引用。 Ie it refers to an existing method by name. 即,它通过名称引用现有方法。

Short Explanation : 简短说明 : Below is an example of a reference to a static method: 下面是对静态方法的引用示例:

代码语言:javascript复制
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 can be passed around just like object references and triggered when needed. square可以像对象引用一样传递,并在需要时触发。 In fact, it can be just as easily used as a reference to “normal” methods of objects as static ones. 实际上,它可以像引用static对象一样容易地用作对对象“常规”方法的引用。 For example: 例如:

代码语言:javascript复制
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 above is a functional interface . Function上面是一个功能性的接口 。 To fully understand :: , it is important to understand functional interfaces as well. 要完全理解:: ,同样重要的是要了解功能接口。 Plainly, a functional interface is an interface with just one abstract method. 显然, 功能接口是仅具有一种抽象方法的接口。

Examples of functional interfaces include Runnable , Callable , and ActionListener . 功能接口的示例包括RunnableCallableActionListener

Function above is a functional interface with just one method: apply . Function以上是只用一个方法的功能的接口: apply It takes one argument and produces a result. 它接受一个参数并产生结果。


The reason why :: s are awesome is that : 为什么原因:: s为真棒是

Method references are expressions which have the same treatment as lambda expressions (…), but instead of providing a method body, they refer an existing method by name. 方法引用是与lambda表达式(…)相同的表达式,但是它们没有提供方法主体,而是通过名称引用了现有方法。

Eg instead of writing the lambda body 例如,而不是编写lambda正文

代码语言:javascript复制
Function<Double, Double> square = (Double x) -> x * x;

You can simply do 你可以做

代码语言:javascript复制
Function<Double, Double> square = Hey::square;

At runtime, these two square methods behave exactly the same as each other. 在运行时,这两个square方法的行为彼此完全相同。 The bytecode may or may not be the same (though, for the above case, the same bytecode is generated; compile the above and check with javap -c ). 字节码可以相同也可以不相同(不过,对于上述情况,会生成相同的字节码;请编译以上内容并使用javap -c检查)。

The only major criterion to satisfy is: the method you provide should have a similar signature to the method of the functional interface you use as object reference . 要满足的唯一主要标准是: 您提供的方法应该与用作对象引用的功能接口的方法具有相似的签名

The below is illegal: 以下是非法的:

代码语言:javascript复制
Supplier<Boolean> p = Hey::square; // illegal

square expects an argument and returns a double . square需要一个参数并返回double The get method in Supplier returns a value but does not take an argument. Supplier中的get方法返回一个值,但不接受参数。 Thus, this results in an error. 因此,这导致错误。

A method reference refers to the method of a functional interface. 方法参考是指功能接口的方法。 (As mentioned, functional interfaces can have only one method each). (如前所述,每个功能接口只能有一种方法)。

Some more examples: the accept method in Consumer takes an input but doesn’t return anything. 还有更多示例: Consumer中的accept方法accept输入但不返回任何内容。

代码语言:javascript复制
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

Above, getRandom takes no argument and returns a double . 上面的getRandom不带参数,返回double So any functional interface that satisfies the criteria of: take no argument and return double can be used. 因此,可以使用满足以下条件的任何功能接口: 不带参数并返回double

Another example: 另一个例子:

代码语言:javascript复制
Set<String> set = new HashSet<>();
set.addAll(Arrays.asList("leo","bale","hanks"));
Predicate<String> pred = set::contains;
boolean exists = pred.test("leo");

In case of parameterized types : 如果是参数化类型 :

代码语言:javascript复制
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;

Method references can have different styles, but fundamentally they all mean the same thing and can simply be visualized as lambdas: 方法引用可以具有不同的样式,但是从根本上讲,它们都表示同一件事,并且可以简单地可视化为lambda:

  1. A static method ( ClassName::methName ) 静态方法( ClassName::methName
  2. An instance method of a particular object ( instanceRef::methName ) 特定对象的实例方法( instanceRef::methName
  3. A super method of a particular object ( super::methName ) 特定对象的超级方法( super::methName
  4. An instance method of an arbitrary object of a particular type ( ClassName::methName ) 特定类型( ClassName::methName )的任意对象的实例方法
  5. A class constructor reference ( ClassName::new ) 类构造函数参考( ClassName::new
  6. An array constructor reference ( TypeName[]::new ) 数组构造函数引用( TypeName[]::new

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

#6楼

:: is a new operator included in Java 8 that is used to refer a method of an existing class. ::是Java 8中包含的新运算符,用于引用现有类的方法。 You can refer static methods and non-static methods of a class. 您可以引用类的静态方法和非静态方法。

For referring static methods, the syntax is: 对于引用静态方法,语法为:

代码语言:javascript复制
ClassName :: methodName 

For referring non-static methods, the syntax is 对于引用非静态方法,语法为

代码语言:javascript复制
objRef :: methodName

And

代码语言:javascript复制
ClassName :: methodName

The only prerequisite for referring a method is that method exists in a functional interface, which must be compatible with the method reference. 引用方法的唯一前提条件是该方法存在于功能接口中,该接口必须与方法引用兼容。

Method references, when evaluated, create an instance of the functional interface. 方法引用在评估后会创建功能接口的实例。

Found on: http://www.speakingcs.com/2014/08/method-references-in-java-8.html 发现于: http : //www.speakingcs.com/2014/08/method-references-in-java-8.html

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/156445.html原文链接:https://javaforall.cn

0 人点赞