Implementing a Consumer (or any other functional interface) in a class of its own in a centralized way sometimes has its utility, such as not spreading the same code at several points, you have statement lambda and not just a simple lambda expression.
Given their examples, because they are trivial, they really do not show us any improvement. In addition, in its examples it is not necessary to make explicit the body as it did - even if it is to centralize the implementation - lambdas took this verbosity of java, so when you have a functional interface you do not need to explicitly sign the implemented method. >
Some examples of advantages in implementing a Consumer
(or any other functional interface) are:
-
own functional interfaces, with one or more methods (only one non-default, of course). In these cases you may want to centralize the implementation.
-
use the consumer / / function / etc. to do repetitive things in some points of the code, like this:
public static void main(String[] args) {
final Consumer<Usuario> printName = Usuario::printName;
final Usuario u1 = new Usuario();
u1.name = "Bruno";
final Usuario u2 = new Usuario();
u2.name = "César";
printName.accept(u1);
printName.accept(u2);
}
private static class Usuario {
String name;
void printName() {
System.out.println(name);
}
}
That is, we are applying the same consumer (again, it can be any functional interface) to several objects, in some contexts this is interesting.
This is very useful when we have functions that we apply in several points and centralizing, that is, functions constant, something like this:
public interface MathFunctions {
Function<Integer, Integer> funcMultiplicaPor2 = x -> x * 2;
}
public static void main(String[] args) {
System.out.println(MathFunctions.funcMultiplicaPor2.apply(4));
System.out.println(MathFunctions.funcMultiplicaPor2.apply(8));
}
- same functional implementation on several points: imagine that you have a
Consumer
even though the work it does is the same in several points of the code, something like this:
usuarios.forEach(u -> {
u.ativar();
u.setDataHoraAtivacao(LocalDateTime.now());
// mais alguma coisa
});
Instead of spreading this through the code you can have a Consumer
(or, again, any other functional interface) in Usuario
and just reference it:
final Consumer<Usuario> ATIVACAO_CONSUMER = u -> {
u.ativar();
u.setDataHoraAtivacao(LocalDateTime.now());
// mais alguma coisa
};
usuarios.forEach(Usuario.ATIVACAO_CONSUMER);
This kind of thing is useful when we have Function
that we reference in several parts, as cases that we have a function to map a generic type T to another type, for example. Another case is when we have a Comparator
than the default implemented , something like this:
public static final Comparator<Usuario> USUARIO_COMPARATOR = Comparator.comparing(Usuario::getNome).thenComparing(Usuario::getCPF);
To use this way:
final Set<Usuario> usuarios = usuarios.stream().sorted(Usuario.USUARIO_COMPARATOR).collect(Collections.toSet());
Note: Note that this is an example, in this case a method that does the activation things may be better;)
In summary: it will depend a lot on what you are using, it usually only affects the organization of the code itself, personal practices, etc., otherwise you do not need to explicitly implement such interfaces. In large applications you can decide to have a design of only functional interfaces (such as predicates and operators) that are shared by the application, utilitarian things and such.
Another point I saw, which you may not yet be familiar with (even though you used it above), in this example:
usuarios.forEach(u -> System.out.println(u));
You do not need to use a lambda expression, you can use method reference :
usuarios.forEach(System.out::println);
In this case, you are referencing a static method that takes as its argument the type parameter of Consumer
informed, inferred by the collection.