3

someone can help me understand below code from Java 8 Functional Interface As per my understanding accept() takes as input and process it but does not return any value then in case of andThen() how it works

accept() method takes as input the type T and returns no value.

default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
}
Naman
  • 27,789
  • 26
  • 218
  • 353
NPE
  • 429
  • 2
  • 16
  • 2
    `(T t) -> { accept(t); after.accept(t); }` is a `Consumer` again which ensures that the current Consumer is `accept`ed first and then the one mentioned as `after` is `accept`ed. – Naman Aug 02 '19 at 12:00
  • but accept method is not returning any value then how come we get Consumer? – NPE Aug 02 '19 at 12:02
  • @NPE the returned value is the whole thing: `(T t) -> { accept(t); after.accept(t); }`, not the result of the call to `accept(t)`. – Maurice Perry Aug 02 '19 at 12:05

3 Answers3

1

In order to understand what is getting returned from that API, you can try visualizing the implementation as :

default Consumer<T> andThen(Consumer<? super T> after) {
    Objects.requireNonNull(after);
    return new Consumer<T>() { // return the complete Consumer implementation
        @Override
        public void accept(T t) {
            Consumer.this.accept(t); // accept current consumer
            after.accept(t); // and then accept the 'after' one.
        }
    };
}

and now relate that

(T t) -> { accept(t); after.accept(t); } 

is a Consumer returned which ensures that the current one is accepted first and then the one mentioned as after.

Naman
  • 27,789
  • 26
  • 218
  • 353
  • `@Naman` Why we have to do Consumer.this.accept(t)? I did not get this. – IfOnly Aug 07 '19 at 14:04
  • 1
    @IfOnly It's a ["qualified this"](https://docs.oracle.com/javase/specs/jls/se12/html/jls-15.html#jls-15.8.4), which makes it so the `accept` method of the _enclosing_ instance is invoked. Without the qualification the `accept` method of the current instance (`this`) would be invoked, leading to infinite recursion. It's a little confusing in this case, since we have an anonymous `Consumer` enclosed by another `Consumer`, but it's no different than the example given in [this Q&A](https://stackoverflow.com/questions/11276994/what-does-qualified-this-construct-mean-in-java). – Slaw Aug 10 '19 at 21:42
1

A functional interface must have only one abstract method. However, it can have as many static and default methods as you'd like. The methods of Consumer are:

  1. accept(T)

    • This is the single abstract method of Consumer. It accepts a single generic argument of type T and returns nothing (i.e. void). This is the method that's implemented by a lambda expression or method reference.
  2. andThen(Consumer)

    • This is a default method. In other words, it has an implementation and is thus non-abstract. The method accepts a Consumer and returns another Consumer. Since it's a default method, the single abstract method of Consumer remains accept(T).

The above explains why Consumer can have a method that returns something other than void. Now, when it comes to the implementation of andThen, it's important to realize there are actually three Consumers involved:

  1. The instance on which andThen was invoked.
  2. The instance referenced by after.
  3. The instance returned to the caller.

If you format the code so not everything is on the same line it may be easier to follow:

default Consumer<T> andThen(Consumer<? super T> after) {
    Objects.requireNonNull(after);
    // Returns Consumer instance #3. The lambda is the implementation
    // of the 'accept' method.
    return (T t) -> {
        accept(t);       // Invokes 'accept' on Consumer instance #1.
        after.accept(t); // Invokes 'accept' on Consumer instance #2.
    }
}
Slaw
  • 37,820
  • 8
  • 53
  • 80
0

I think I understand your concern. You wonder why could you call andThen() after accept() if accept() returns void.

The thing is, that you first define a Consumer object, and on that instance you call andThen() method. For example:

Consumer<String> consumer = s -> System.out.println(s);
consumer.andThen(s -> System.out.println(s.toUpperCase()));        
mate00
  • 2,727
  • 5
  • 26
  • 34