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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20001427/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 22:12:31  来源:igfitidea点击:

:: (double colon) operator in Java 8

javajava-8

提问by Narendra Pathai

I was exploring the Java 8source and found this particular part of code very surprising:

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

Is Math::maxsomething like a method pointer? How does a normal staticmethod gets converted to IntBinaryOperator?

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

采纳答案by isnot2bad

Usually, one would call the reducemethod using Math.max(int, int)as follows:

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

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. That's where lambda expressions come into play. Since Java 8 it is allowed to do the same thing in a much shorter way:

这需要很多语法才能调用Math.max. 这就是 lambda 表达式发挥作用的地方。从 Java 8 开始,允许以更短的方式做同样的事情:

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 ints and returns one int. This is equivalent to the formal parameters of the one and only method of interface IntBinaryOperator(the parameter of method reduceyou want to call). So the compiler does the rest for you - it just assumes you want to implement IntBinaryOperator.

这是如何运作的?java 编译器“检测到”,您要实现一个方法,该方法接受两个ints 并返回一个 s int。这相当于接口的唯一方法IntBinaryOperator的形参(reduce你要调用的方法的参数)。因此,编译器会为您完成剩下的工作——它只是假设您要实现IntBinaryOperator.

But as Math.max(int, int)itself fulfills the formal requirements of IntBinaryOperator, it can be used directly. 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:

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

reduce(Math::max);

Note that this will be interpreted by the compiler, not by the JVM at runtime! 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 IntBinaryOperatorimplementation above!

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

(See also Translation of Lambda Expressions)

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

回答by Olimpiu POP

Yes, that is true. The ::operator is used for method referencing. So, one can extract staticmethods 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.

可以在此处找到 Oracle 的官方文档。

You can have a better overview of the JDK 8 changes in thisarticle. In the Method/Constructor referencingsection a code example is also provided:

您可以在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);
   }
}

回答by david99world

This is a method reference in Java 8. The oracle documentation is here.

这是 Java 8 中的方法参考。oracle 文档在这里

As stated in the documentation...

如文档中所述...

The method reference Person::compareByAge is a reference to a static method.

The following is an example of a reference to an instance method of a particular object:

方法引用 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); 

The method reference myComparisonProvider::compareByName invokes the method compareByName that is part of the object myComparisonProvider. The JRE infers the method type arguments, which in this case are (Person, Person).

方法引用 myComparisonProvider::compareByName 调用方法 compareByName,它是对象 myComparisonProvider 的一部分。JRE 推断方法类型参数,在本例中为 (Person, Person)。

回答by Jatin

::is called Method Reference. It is basically a reference to a single method. I.e. it refers to an existing method by name.

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

Short Explanation:
Below is an example of a reference to a static method:

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

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

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

squarecan be passed around just like object references and triggered when needed. In fact, it can be just as easily used as a reference to "normal" methods of objects as staticones. For example:

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

Functionabove is a functional interface. To fully understand ::, it is important to understand functional interfaces as well. Plainly, a functional interfaceis an interface with just one abstract method.

Function上面是一个功能界面。要完全理解::,理解函数式接口也很重要。简单来说,函数式接口是只有一个抽象方法的接口。

Examples of functional interfaces include Runnable, Callable, and ActionListener.

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

Functionabove is a functional interface with just one method: apply. It takes one argument and produces a result.

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



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 表达式 (...) 具有相同处理方式的表达式,但它们不提供方法主体,而是按名称引用现有方法。

E.g. instead of writing the lambda body

例如,而不是编写 lambda 主体

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

You can simply do

你可以简单地做

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

At runtime, these two squaremethods behave exactly the same as each other. 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).

在运行时,这两个square方法的行为完全相同。字节码可能相同也可能不同(不过,对于上述情况,会生成相同的字节码;编译上述内容并使用 进行检查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:

以下内容是非法的:

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

squareexpects an argument and returns a double. The getmethod in Supplierreturns a value but does not take an argument. Thus, this results in an error.

square需要一个参数并返回一个double. 供应商中get方法返回一个值但不带参数。因此,这会导致错误。

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 acceptmethod in Consumertakes an input but doesn't return anything.

更多示例: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

Above, getRandomtakes no argument and returns a double. So any functional interface that satisfies the criteria of: take no argument and return doublecan be used.

上面,不getRandom接受任何参数并返回一个double. 因此,可以使用任何满足以下条件的函数式接口:不带参数并返回double

Another example:

另一个例子:

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:

在参数化类型的情况下

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:

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

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

回答by sreenath

::is a new operator included in Java 8 that is used to refer a method of an existing class. You can refer static methods and non-static methods of a class.

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

For referring static methods, the syntax is:

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

ClassName :: methodName 

For referring non-static methods, the syntax is

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

objRef :: methodName

And

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

回答by Alfa khatoon

At runtime they behave a exactly the same.The bytecode may/not be same (For above Incase,it generates the same bytecode(complie above and check javaap -c;))

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

At runtime they behave a exactly the same.method(math::max);,it generates the same math (complie above and check javap -c;))

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

回答by Jude Niroshan

return reduce(Math::max);is NOT EQUALto return reduce(max());

return reduce(Math::max);不等于return reduce(max());

But it means, something like this:

但这意味着,像这样:

IntBinaryOperator myLambda = (a, b)->{(a >= b) ? a : b};//56 keystrokes I had to type -_-
return reduce(myLambda);

You can just save 47 keystrokesif you write like this

如果你这样写,你可以节省 47 次击键

return reduce(Math::max);//Only 9 keystrokes ^_^

回答by Sonu

The :: is known as method references. Lets say we want to call a calculatePrice method of class Purchase. Then we can write it as:

:: 被称为方法引用。假设我们要调用类 Purchase 的 calculatePrice 方法。那么我们可以写成:

Purchase::calculatePrice

It can also be seen as short form of writing the lambda expression Because method references are converted into lambda expressions.

它也可以看作是编写 lambda 表达式的简短形式,因为方法引用被转换为 lambda 表达式。

回答by HRgiger

Since many answers here explained well ::behaviour, additionally I would like to clarify that ::operator doesnt need to have exactly same signature as the referring Functional Interface if it is used for instance variables. Lets assume we need a BinaryOperatorwhich has type of TestObject. In traditional way its implemented like this:

由于这里的许多答案都解释了良好的::行为,另外我想澄清一下,如果::运算符用于实例变量,则它不需要与引用的功能接口具有完全相同的签名。假设我们需要一个类型为TestObjectBinaryOperator。以传统方式实现如下:

BinaryOperator<TestObject> binary = new BinaryOperator<TestObject>() {

        @Override
        public TestObject apply(TestObject t, TestObject u) {

            return t;
        }
    };

As you see in anonymous implementation it requires two TestObject argument and returns a TestObject object as well. To satisfy this condition by using ::operator we can start with a static method:

正如您在匿名实现中看到的,它需要两个 TestObject 参数并返回一个 TestObject 对象。为了通过使用::运算符来满足这个条件,我们可以从一个静态方法开始:

public class TestObject {


    public static final TestObject testStatic(TestObject t, TestObject t2){
        return t;
    }
}

and then call:

然后调用:

BinaryOperator<TestObject> binary = TestObject::testStatic;

Ok it compiled fine. What about if we need an instance method? Lets update TestObject with instance method:

好的,它编译得很好。如果我们需要一个实例方法呢?让我们用实例方法更新 TestObject:

public class TestObject {

    public final TestObject testInstance(TestObject t, TestObject t2){
        return t;
    }

    public static final TestObject testStatic(TestObject t, TestObject t2){
        return t;
    }
}

Now we can access instance as below:

现在我们可以访问实例如下:

TestObject testObject = new TestObject();
BinaryOperator<TestObject> binary = testObject::testInstance;

This code compiles fine, but below one not:

这段代码编译得很好,但下面的代码不是:

BinaryOperator<TestObject> binary = TestObject::testInstance;

My eclipse tell me "Cannot make a static reference to the non-static method testInstance(TestObject, TestObject) from the type TestObject ..."

我的日食告诉我“无法从 TestObject 类型对非静态方法 testInstance(TestObject, TestObject) 进行静态引用......”

Fair enough its an instance method, but if we overload testInstanceas below:

它是一个实例方法,但如果我们重载testInstance如下:

public class TestObject {

    public final TestObject testInstance(TestObject t){
        return t;
    }

    public final TestObject testInstance(TestObject t, TestObject t2){
        return t;
    }

    public static final TestObject testStatic(TestObject t, TestObject t2){
        return t;
    }
}

And call:

并调用:

BinaryOperator<TestObject> binary = TestObject::testInstance;

The code will just compile fine. Because it will call testInstancewith single parameter instead of double one. Ok so what happened our two parameter? Lets printout and see:

代码会很好地编译。因为它将testInstance使用单个参数而不是双参数调用。好的,那么我们的两个参数发生了什么?让我们打印出来看看:

public class TestObject {

    public TestObject() {
        System.out.println(this.hashCode());
    }

    public final TestObject testInstance(TestObject t){
        System.out.println("Test instance called. this.hashCode:" 
    + this.hashCode());
        System.out.println("Given parameter hashCode:" + t.hashCode());
        return t;
    }

    public final TestObject testInstance(TestObject t, TestObject t2){
        return t;
    }

    public static final TestObject testStatic(TestObject t, TestObject t2){
        return t;
    }
}

Which will output:

这将输出:

 1418481495  
 303563356  
 Test instance called. this.hashCode:1418481495
 Given parameter hashCode:303563356

Ok so JVM is smart enough to call param1.testInstance(param2). Can we use testInstancefrom another resource but not TestObject, i.e.:

好的,JVM 足够聪明,可以调用 param1.testInstance(param2)。我们可以testInstance从另一个资源而不是 TestObject 使用,即:

public class TestUtil {

    public final TestObject testInstance(TestObject t){
        return t;
    }
}

And call:

并调用:

BinaryOperator<TestObject> binary = TestUtil::testInstance;

It will just not compile and compiler will tell: "The type TestUtil does not define testInstance(TestObject, TestObject)". So compiler will look for a static reference if it is not the same type. Ok what about polymorphism? If we remove final modifiers and add our SubTestObjectclass:

它不会编译,编译器会告诉:"TestUtil 类型没有定义 testInstance(TestObject, TestObject)"。所以编译器会寻找一个静态引用,如果它不是相同的类型。好吧,多态呢?如果我们删除 final 修饰符并添加我们的SubTestObject类:

public class SubTestObject extends TestObject {

    public final TestObject testInstance(TestObject t){
        return t;
    }

}

And call:

并调用:

BinaryOperator<TestObject> binary = SubTestObject::testInstance;

It will not compile as well, compiler will still look for static reference. But below code will compile fine since it is passing is-a test:

它也不会编译,编译器仍然会寻找静态引用。但是下面的代码可以很好地编译,因为它正在通过 is-a 测试:

public class TestObject {

    public SubTestObject testInstance(Object t){
        return (SubTestObject) t;
    }

}

BinaryOperator<TestObject> binary = TestObject::testInstance;

*I am just studying so I have figured out by try and see, feel free to correct me if I am wrong

*我只是在学习,所以我已经通过尝试了解了,如果我错了,请随时纠正我

回答by Vaibhav9518

:: Operatorwas introduced in java 8 for method references. A method reference is the shorthand syntax for a lambda expression that executes just ONE method. Here's the general syntax of a method reference:

:: 运算符是在 java 8 中引入的用于方法引用。方法引用是只执行一个方法的 lambda 表达式的简写语法。以下是方法引用的一般语法:

Object :: methodName

We know that we can use lambda expressionsinstead of using an anonymous class. But sometimes, the lambda expression is really just a call to some method, for example:

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

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

To make the code clearer, you can turn that lambda expression into a method reference:

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

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