Java8中的::(双冒号)运算符
本⽂翻译⾃:
I was exploring the Java 8 source and found this particular part of code very surprising: 我正在探索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;
}
Is Math::max something like a method pointer?Math::max是否类似于⽅法指针?
是否类似于⽅法指针? How does a normal static method get converted to IntBinaryOperator ? 普通的
普通的static⽅法如何转换为IntBinaryOperator ?
#1楼
#2楼
Yes, that is true. 是的,那是真的。
运算符⽤于⽅法引⽤。 So, one can 是的,那是真的。 The :: operator is ud for method referencing.::运算符⽤于⽅法引⽤。
extract static methods from class by using it or methods from objects. 因此,可以通过使⽤静态⽅法或对象的⽅法来提取静
因此,可以通过使⽤静态⽅法或对象的⽅法来提取静
即使对于构造函数,也可以使⽤相同的运算符。 All cas
态⽅法。 The same operator can be ud even for constructors. 即使对于构造函数,也可以使⽤相同的运算符。
态⽅法。
mentioned here are exemplified in the code sample below. 此处提到的所有情况在下⾯的代码⽰例中得到了⽰例。
此处提到的所有情况在下⾯的代码⽰例中得到了⽰例。
The official documentation from Oracle can be found . Oracle的官⽅⽂档可以在找到。
Oracle的官⽅⽂档可以在找到。
You can have a better overview of the JDK 8 changes in article. 您可以在JDK 8的变化,更好地观察⽂章。
您可以在JDK 8的变化,更好地观察⽂章。 In the
Method/Constructor referencing ction a code example is also provided: 在“ ⽅法/构造函数引⽤”部分中,还提供了代码⽰
在“ ⽅法/构造函数引⽤”部分中,还提供了代码⽰例:
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 args) {
// constructor reference
ConstructorReference reference = ConstructorClass::new;
ConstructorClass cc = structor();
// 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 . 这是Java 8中的⽅法参考。oracle⽂档在 。
这是Java 8中的⽅法参考。oracle⽂档在 。
As stated in 如⽂档中所述...
如⽂档中所述...
The method reference Person::compareByAge is a reference to a static method. ⽅法引⽤Person :: compareByAge是
⽅法引⽤Person :: compareByAge是对静态⽅法的引⽤。
The following is an example of a reference to an instance method of a particular object: 以下是对特
定对象的实例⽅法的
以下是对特定对象的实例⽅法的引⽤⽰例:
class ComparisonProvider {
public int compareByName(Person a, Person b) {
Name().Name());
}
public int compareByAge(Person a, Person b) {
Birthday().Birthday());
}
}
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调⽤⽅法公路隧道
⽅法引⽤myComparisonProvider :: compareByName调⽤⽅法compareByName,它是对象myComparisonProvider的⼀部分。 The JRE infers the method type arguments, which compareByName,它是对象myComparisonProvider的⼀部分。
in this ca are (Person, Person). JRE推断⽅法类型参数,在这种情况下为(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)如下所⽰:
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
仅调⽤Math.max需要⼤量语法。
come into play. 那就是lambda表达式起作⽤的地⽅。
那就是lambda表达式起作⽤的地⽅。 Since Java 8 it is allowed to do the same thing in a much shorter way:
从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曹植七步成诗
。 This is equivalent to the formal
int s and returns one int . Java编译器“检测”您要实现⼀个接受两个重启手机
Java编译器“检测”您要实现⼀个接受两个int并返回⼀个int 。
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) itlf fulfills the formal requirements of IntBinaryOperator , it can be ud directly. 但是由
但是由
的形式要求,因此可以直接使⽤它。 Becau Java 7 does not have any syntax
于Math.max(int, int)本⾝满⾜IntBinaryOperator的形式要求,因此可以直接使⽤它。
that allows a method itlf to be pasd 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 7没有任何语法允许将⽅法本⾝作为参数传递(您只能传递⽅法结果,⽽不能传递⽅法引⽤),因此Java 8中引⼊了::语法来引⽤⽅法:
reduce(Math::max);
Note that this will be interpreted by the compiler, not by the JVM at runtime! 注意,这将由编译器⽽不是运⾏时的JVM解释!
注意,这将由编译器⽽不是运⾏时的JVM解释!Although it produces different bytecodes for all three code snippets, they are mantically 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 ) (另请参见 )
(另请参见 )
#5楼
它基本上是对单个⽅法的引⽤。
称为⽅法参考。 It is basically a reference to a single method. 它基本上是对单个⽅法的引⽤。:: is called Method Reference.::称为⽅法参考。
Ie 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);
square can be pasd around just like object references and triggered when needed.square可以像对象引⽤⼀样传递,并在需
实际上,它可以要时触发。
要时触发。 In fact, it can be just as easily ud as a reference to "normal" methods of objects as static ones. 实际上,它可以
例如:
像引⽤static对象⼀样容易地⽤作对对象“常规”⽅法的引⽤。
对象⼀样容易地⽤作对对象“常规”⽅法的引⽤。 For example: 例如:
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);
上⾯是⼀个功能性的接⼝ 。 To fully understand :: , it is important to Function above is a functional interface .Function上⾯是⼀个功能性的接⼝ 。
understand functional interfaces as well. 要完全理解
,同样重要的是要了解功能接⼝。 Plainly, a is an interface with just one
要完全理解:: ,同样重要的是要了解功能接⼝。
abstract method. 显然, 是仅具有⼀种抽象⽅法的接⼝。
显然, 是仅具有⼀种抽象⽅法的接⼝。
Examples of functional interfaces include Runnable , Callable , and ActionListener . 功能接⼝的⽰例包括
功能接⼝的⽰例包括Runnable ,
Callable和ActionListener 。
。 It Function above is a functional interface with just one method: apply .Function以上是只⽤⼀个⽅法的功能的接⼝: apply 。takes one argument and produces a result. 它接受⼀个参数并产⽣结果。
它接受⼀个参数并产⽣结果。
The reason why :: s are awesome is : 为什么原因
为什么原因:: 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表达式(...)相同的表达式,但是
⽅法引⽤是与lambda表达式(...)相同的表达式,但是它们没有提供⽅法主体,⽽是通过名称引⽤了现有⽅法。
Eg instead of writing the lambda body 例如,⽽不是编写lambda正⽂
例如,⽽不是编写lambda正⽂
Function<Double, Double> square = (Double x) -> x * x;
You can simply do 你可以做
你可以做
Function<Double, Double> square = Hey::square;
At runtime, the two square methods behave exactly the same as each other. 在运⾏时,这两个
在运⾏时,这两个square⽅法的⾏为彼此完全相同。 The bytecode may or may not be the same (though, for the above ca, 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 u as object reference . 要满⾜的唯⼀主要标准是:
要满⾜的唯⼀主要标准是: 您提供的⽅法应该与⽤作对象引⽤的功能接⼝的⽅法具有相似的签名 。
The below is illegal: 以下是⾮法的:
以下是⾮法的:
Supplier<Boolean> p = Hey::square; // illegal
。 The get method in returns a value but square expects an argument and returns a double .square需要⼀个参数并返回double 。
does not take an argument. 的的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 takes an input but doesn't return anything. 还有更多⽰例: 的
还有更多⽰例: 的accept⽅法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 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 ud. 因此,可以使⽤满⾜以下条件的任何功
因此,可以使⽤满⾜以下条件的任何功能接⼝: 不带参数并返回double 。
Another example: 另⼀个例⼦:
另⼀个例⼦:
Set<String> t = new HashSet<>();
t.addAll(Arrays.asList("leo","bale","hanks"));
Predicate<String> pred = t::contains;
boolean exists = st("leo");