@FunctionalInterface
interface StringConsumer extends Consumer<String> {
@Override
public default void accept(String s) {
System.out.println(s.toUpperCase());
}
void print(String s);
}
public class Test {
public static void main(String[] args) {
StringConsumer stringConsumerImpl= s -> System.out.println(s.toLowerCase());
List<String> list = Arrays.asList("Dr", "Mr", "Miss", "Mrs");
list.forEach(stringConsumerImpl);
}
}
Output: DR MR MISS MRS
So what is going on in this code is really weird for me. I am actually providing implementation of print method but when I run the code it actually uses implementation of default method.
I was expecting print implementation to work. edit: when print method is deleted from StringConsumer it causes compilation error on main method
Iterable.forEach
is implemented like this:
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
It calls the accept
method of the Consumer
you passed in, so that is why accept
is called and not print
. It is as simple as that.
You might have thought that forEach
will call whatever lambda expression you passed in. This is not true. Lambda expressions are just a fancy way to create an implementation of an interface that has a single abstract method. The body of the lambda expression is the implementation of that single abstract method (print
). It just saves you the trouble of writing a class like this and creating an instance of it:
class Foo implements StringConsumer {
@Override
public void print(String s) {
System.out.println(s.toLowerCase());
}
}
That interface is free to have other default methods and other methods can choose to call those default methods, instead of the abstract method the lambda implements. It's just like every other variable of an interface type.