:: (double colon) operator in Java 8
I was exploring the Java 8 source and found this particular part of code very surprising:
//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? How does a normal static
method get converted to IntBinaryOperator
?
Usually, one would call the reduce
method using Math.max(int, int)
as follows:
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:
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
. 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). So the compiler does the rest for you - it just assumes you want to implement 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:
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 IntBinaryOperator
implementation above!
(See also Translation of Lambda Expressions)
::
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:
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 reference's and trigger when at need. In fact, it can be perfectly used as a reference to a normal method of an object and not just static
ones.
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 . Well to fully explain ::
, it is important to understand functional interface. Plainly, function interface is an interface with just one abstract method.
For example: Runnable
, Callable
, ActionListener
and so.
Function
above is a functional interface with just one method apply
. It takes one argument and produces a result.
The reason why ::
are awesome is because:
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.
ie Just like writing lambda body:
Function<Double, Double> square = (Double x) -> x * x;
You can simply do:
Function<Double, Double> square = Hey::square;
At runtime they behave exactly the same. The bytecode may/not be the same (For above case, it generates the same bytecode (compile above and check javap -c
))
The only major criteria to satisfy is: the method you provide should have a similar signature to method of the functional interface you use as object reference .
Below is illegal:
Supplier<Boolean> p = Hey::square; //illegal
square
expects an argument and returns a double. get
method in Supplier expects an argument but doesn't return anything. So it is an error.
Method Reference refers to a method of the functional interface (As mentioned, functional interface can only have one method).
Some more examples: accept
method in Consumer takes an input but doesn't return anything.
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. So any functional interface that satisfies the criteria of: take no argument and return double can be used.
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 Reference can be obtained in different styles, but fundamentally they all mean the same and can simply be visualized as a lambda:
ClassName::methName
) instanceRef::methName
) super::methName
) ClassName::methName
) ClassName::new
) TypeName[]::new
) For further reference: http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html
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. In the Method/Constructor referencing section 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 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);
}
}
链接地址: http://www.djcxy.com/p/36370.html
上一篇: Java 8将<V>列表映射到Map <K,V>中
下一篇: Java 8中的::(双冒号)运算符