What is the purpose of the symbol :: in Java?

Asked

Viewed 6,210 times

23

I implemented a method that sums all the numbers in a list of the kind List<Integer> as follows:

int soma = 0;

for (Integer num : numeros)
    soma += num;
return soma;

However, the Netbeans IDE suggested that I replace the above routine with the following routine below:

int soma = 0;
return numeros.stream().map((numero) -> numero).reduce(soma, Integer::sum);

I know that the above routine uses new features of Java 8, however, I was in doubt about the symbol :: used in the method reduce(), I don’t know if it’s some kind of operator, I’d like to know what its purpose is?

  • Until I could understand the call in lambda, I just can’t explain it to you, otherwise I’d answer :/

  • @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.

  • @jbueno is a Stream class method, which by the way is still obscure to me, since it is recent.

  • 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

3 answers

28


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:

  1. Starting value
  2. 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();
  • 1

    I was waiting for an answer from you =)

  • Just out of curiosity, why Supplier<Integer> in place of IntSupplier?

5

It is a new feature of the language in version 8. Call Method Reference or Method Reference. The feature provides a way to reference a method without executing it. In addition, it is related to lambda expressions because, like them, it requires a target type context composed of a compatible functional interface. When a Method Reference is evaluated, an instance of a functional interface is created. This beautiful definition I took from the book of Schildt

An example:

@Test
public void remove_null_from_list_java8_lambda () {
    List<String> strings = Lists.newArrayList(null, "www", null, "leveluplunch", "com", null);

    List<String> filterStrings = strings
        .stream()
        .filter(p -> p != null)
        .collect(Collectors.toList());

    assertEquals(3, filterStrings.size());

    // or
    List<String> filterStrings2 = strings
        .stream()
        .filter(Objects::nonNull)
        .collect(Collectors.toList());

    assertEquals(3, filterStrings2.size());
}

The line .filter(Objects::nonNull) is calling the method Objects.nonNull(Object) passing as parameter an object (the current) of the list. That is, declared the method without explicitly invoking it.

The example I took from here.

More information on Java tutorial (in English).

  • If I remember any better/more didactic examples I update the answer.

3

This operator is called Method Reference.

In a nutshell it serves to reference the method sum class Integer. In this case, you are passing the method sum as the second parameter of reduce.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.