The operator ::
was added to Java 8 and is part of expressions that reference methods (Method Reference Expressions).
References to methods (Method References) to complement the Ambids.
A lambda is like a chunk of code that you can pass as an argument to a method. Only sometimes this chunk of code repeats or is already implemented in some method, so you can simply reference any method.
According to the documentation, there are basically four types of references to methods:
1. Static method
public static void medirTempo(Runnable rotinaDemorada, Supplier<Long> timestampSupplier) {
long inicio = timestampSupplier.get();
rotinaDemorada.run();
System.out.format("Tempo: %d ms", timestampSupplier.get() - inicio);
}
Runnable
is a functional interface that abstracts a task to be executed. It neither receives nor returns value. Supplier
is a functional interface whose execution returns a value of a certain type, Long
in this case.
Given a routine that takes:
Runnable rotinaDemorada = () -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
};
We can call the method that:
medirTempo(rotinaDemorada, System::currentTimeMillis);
I put the lambda in a separate variable for didactic purposes, but we could pass it directly as parameter.
So we passed a lambda and then the static method currentTimeMillis
class System
as arguments. When the measurement routine performs, timestampSupplier.get()
is exactly the same as System.currentTimeMillis()
. The static method is called twice, at the beginning and at the end, where the time difference is then printed on the console.
Note that System.currentTimeMillis()
does not receive parameters and returns a long number, so it is compatible with the functional interface. The same goes for the lambda that is assigned to the variable Runnable
, that is, it has no parameters and returns no value.
2. Method of a specific instance
Example:
public int tamanho(Supplier<Integer> s) {
return s.get();
}
Supplier
is a functional interface whose execution returns a value of a certain type, Integer
in this case.
We can call the method that:
String s = "Hello, Instance Method!";
int t = tamanho(s::length);
Here we pass the method length
of the object s
as argument. When the routine executes, s.get()
is exactly the same as s.length()
. The size of String
is returned.
Note that s.length()
does not receive parameters and returns an integer, so it is compatible with the functional interface.
Another interesting point is that you could pass List.size ()
for routine with the functional interface. Therefore, the interface compatibility mechanism allows for another generic programming level.
3. A class method applied to any instance
Example:
public int tamanho(Function<String, Integer> f) {
return f.apply("Hello, Class Method!");
}
Function
is a functional interface that receives a value of type T
(String
) and returns a value of type R
(Integer
).
We can call the method that:
int t = tamanho(String::length);
Here we pass the method length
class String
as argument. When the routine executes, f.apply("...")
is exactly the same as calling the method "...".length()
, that is, in the String
passed as argument to apply
.
Note that in the previous example, the functional interface does not know to which object the method belongs. In this example, we can call the method in any instance of the type.
4. Reference to a manufacturer
Example:
public String newHello(Function<String, String> f) {
return f.apply("Hello, Constructor!");
}
Function
is a functional interface that receives a value of type T
(String
) and returns a value of type R
(also String
).
We can call the method that:
String s = newHello(String::new);
Here we pass the builder String(String)
as argument. When the routine executes, f.apply("...")
is exactly the same as instantiating the String like this: new String("...")
.
In practice, there is not much difference between this approach and the previous one, except that using constructors facilitates the use of immutable objects such as String
, BigDecimal
, Integer
and others when using the functional API.
About the Netbeans Transformation
The code generated by the IDE is not good:
int soma = 0;
return numeros.stream().map((numero) -> numero).reduce(soma, Integer::sum);
Unnecessary mapping
I don’t know why the IDE generated a method call map
. It serves basically when you want to transform all the values of a stream something else, generating a new stream. In this case it’s totally unnecessary.
The code below generates the same result:
int soma = 0;
return numeros.stream().reduce(soma, Integer::sum);
Unnecessary variable
The variable soma
is not required as it defines only an initial value.
The code below generates the same result:
return numeros.stream().reduce(0, Integer::sum);
Alternative: IntStream.sum()
The API of Streams Java 8 makes it possible to execute a certain operation in a sequence of values and to aggregate the result in some way. This is analogous to SQL aggregation operations as SUM
and AVG
.
One example is the method reduce
that has the parameters:
- Starting value
- Aggregation method
However, there are shortcuts to common operations in numbers such as class IntStream
implementing sum
, min
, max
, average
and count
.
In the documentation of sum
, read that it is equivalent to reduce(0, Integer::sum)
. So we could rewrite the code like this:
return numeros.stream().mapToInt(Integer::intValue).sum();
Alternative: array of primitives
The matpToInt
in the previous example it is necessary because the stream initial was not of the type IntStream
.
That wouldn’t be necessary if instead of List<Integer>
you have a primitive array int[]
.
Example:
int[] numerosArray = new int[] { 1, 2, 3 };
So the code to use the sum
would just:
return IntStream.of(numerosArray).sum();
Until I could understand the call in lambda, I just can’t explain it to you, otherwise I’d answer :/
– user28595
@Diegof if you want, you can complement my answer. I’m by cell phone now and I won’t be able to improve it. Even because I don’t know the method
reduce
.– Jéf Bueno
@jbueno is a Stream class method, which by the way is still obscure to me, since it is recent.
– user28595
Apparently it is a good explanation of the use, if my English were good, I would even summarize the content: http://netjs.blogspot.com.br/2015/06/method-reference-in-java-8.html
– user28595