Using the Consumer Interface

6

What is the advantage of using the Consummer interface of java in this way?

Example: UsuarioConsummer have a class that implements the interface%% Consummer and calls their method acept % following example:

public class UsuarioConsummer implements Consumer<Usuario> {
    @Override
    public void accept(Usuario u) {
        System.out.println(u);  
    }
}

Then I created a Usuario class with some attributes to test this class and created within the main some users within a list. To print I have created a method that receives a list of Usuario and calls the class UsuarioConsummer :

private static void teste01(List<Usuario> usuarios) {
Consumer<Usuario> usuarioConsumer = new Consumer<Usuario>() {
            @Override
            public void accept(Usuario u) {
                System.out.println(u.toString());
            }
        };
        usuarios.forEach(usuarioConsumer);
        usuarios.forEach(System.out::println);
    }

However, for printing I would not need this whole device if I did it this way:

usuarios.forEach(u -> System.out.println(u));

What's the difference between this two ways of printing the list?

    
asked by anonymous 27.03.2016 / 04:13

1 answer

4

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.

    
27.03.2016 / 15:17