Use of the Consumer interface

Asked

Viewed 2,259 times

6

What is the advantage of using the interface Consummer java-like?

Example: I have a class UsuarioConsummer implementing the interface Consummer and calls his way acept, follow suit:

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

Then I created a class Usuario with some attributes to test this class and created within the main some users within a list. To print I 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, to print I wouldn’t need all this apparatus if I did it this way:

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

What’s the difference in this two way of printing the list?

  • Hello, Edson. Welcome to [en.so]! Actually your question is about the use of Windows and not about the interface Consumer. The two codes are equivalent and the ambles are just a simpler way to avoid all that code to implement the interface, what we call.

  • Thanks for the feedback, it’s really much simpler and it covers many lines of code, but is there any scenario where we need to use Consummer ? Or not ?

1 answer

6


Implement a Consumer(or any other functional interface), in a class proper to this, in a centralized way, has sometimes its usefulness, as not to spread the same code in several points, mainly when you have statement lambda and not just a simple lambda expression.

Considering their examples, because they’re trivial, they don’t really show us any improvement. Also, in your examples it is not necessary to explain the body as it did - even if it is centralizing the implementation - Ambdas took this verbosity of java, so when you have a functional interface you do not need to explain the signature of the implemented method.

Some examples of advantage in implementing a Consumer(or any other functional interface) are:

  • functional interfaces of their own, with one or more methods (just not default, obviously). In these cases you may want to centralize the implementation.

  • use the Consumer/Supplier/function/etc. to do repetitive things at some points in 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 content (again, it can be any functional interface) to several objects, in certain contexts this is interesting.

This is very useful when we have functions that we apply at various points and centralizing, that is, constant functions, something like that:

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 at several points: imagine that you have a Consumer even though the work he does is the same at various points in the code, something like this:
usuarios.forEach(u -> {
    u.ativar();
    u.setDataHoraAtivacao(LocalDateTime.now());

    // mais alguma coisa
});

Instead of spreading it through the code you can have a Consumer(or, again, any other functional interface) in Usuario and only refer to it:

final Consumer<Usuario> ATIVACAO_CONSUMER = u -> {
    u.ativar();
    u.setDataHoraAtivacao(LocalDateTime.now());

    // mais alguma coisa
};

usuarios.forEach(Usuario.ATIVACAO_CONSUMER);

This sort of thing is useful when we have Function that we referenced in various parts, as cases we have a function to map a generic type T to another type, for example. Another case is when we have a Comparator not the implemented standard, something like that:

public static final Comparator<Usuario> USUARIO_COMPARATOR = Comparator.comparing(Usuario::getNome).thenComparing(Usuario::getCPF);

To use like this:

final Set<Usuario> usuarios = usuarios.stream().sorted(Usuario.USUARIO_COMPARATOR).collect(Collections.toSet());

Note: realize that this is an example, in this case it may be better a method that does the activation things ;)

In short: it will depend a lot on what you are using, usually only affects code organization, personal practices, etc., otherwise you do not need to explicitly implement such interfaces. In large applications you can decide to have a design only of functional interfaces (such as predicates and operators) that are shared by the application, utilitarian things and such.


Another point I saw, that maybe you are not used to (even if you used it above), in this example:

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

You don’t 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 argument the parameter of the type of the Consumer informed, inferred by the collection.

  • Thank you for your answer took away my question, thank you to everyone who took the time to answer my question.

Browser other questions tagged

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