As always I was looking through JDK 8 sources and found very interesting code:
@Override default void forEachRemaining(Consumer<? super Integer> action) { if (action instanceof IntConsumer) { forEachRemaining((IntConsumer) action); } }
The question is: how Consumer<? super Integer>
could be an instance of IntConsumer
? Because they are in different hierarchy.
I have made similar code snippet to test casting:
public class InterfaceExample { public static void main(String[] args) { IntConsumer intConsumer = i -> { }; Consumer<Integer> a = (Consumer<Integer>) intConsumer; a.accept(123); } }
But it throws ClassCastException
:
Exception in thread "main" java.lang.ClassCastException: com.example.InterfaceExample$$Lambda$1/764977973 cannot be cast to java.util.function.Consumer
You can find this code at java.util.Spliterator.OfInt#forEachRemaining(java.util.function.Consumer)
Since a primitive type can't be a generic type argument, there are versions of the Function interface for the most used primitive types double, int, long, and their combinations in argument and return types: IntFunction, LongFunction, DoubleFunction: arguments are of specified type, return type is parameterized.
They can be implemented and instantiated using anonymous class syntax. For example, take the ITrade functional interface. It has only one abstract method that takes a Trade object and returns a boolean value – perhaps checking the status of the trade or validating the order or some other condition.
It is important to note that a functional interface can have multiple default methods (it can be said concrete methods which are default), but only one abstract method.
The Lambda expression is used to provide the implementation of an interface which has functional interface. It saves a lot of code. In case of lambda expression, we don't need to define the method again for providing the implementation. Here, we just write the implementation code.
Let's see the code below, then you can see why?
class IntegerConsumer implements Consumer<Integer>, IntConsumer { ... }
Any class can implement multi-interfaces, one is Consumer<Integer>
maybe implements another one is IntConsumer
. Sometimes occurs when we want to adapt IntConsumer
to Consumer<Integer>
and to save its origin type (IntConsumer
), then the code looks like as below:
class IntConsumerAdapter implements Consumer<Integer>, IntConsumer { @Override public void accept(Integer value) { accept(value.intValue()); } @Override public void accept(int value) { // todo } }
Note: it's the usage of Class Adapter Design Pattern.
THEN you can use IntConsumerAdapter
both as Consumer<Integer>
and IntConsumer
, for example:
Consumer<? extends Integer> consumer1 = new IntConsumerAdapter(); IntConsumer consumer2 = new IntConsumerAdapter();
Sink.OfInt
is a concrete usage of Class Adapter Design Pattern in jdk-8.The downside of Sink.OfInt#accept(Integer)
is clearly that JVM will throw a NullPointerException
when it accepts a null
value, so that is why Sink
is package visible.
189 interface OfInt extends Sink<Integer>, IntConsumer {
190 @Override
191 void accept(int value);
193 @Override
194 default void accept(Integer i) {
195 if (Tripwire.ENABLED)
196 Tripwire.trip(getClass(), "{0} calling Sink.OfInt.accept(Integer)");
197 accept(i.intValue());
198 }
199 }
I found it why need to cast a Consumer<Integer>
to an IntConsumer
if pass a consumer like as IntConsumerAdapter
?
One reason is when we use a Consumer
to accept an int
the compiler needs to auto-boxing it to an Integer
. And in the method accept(Integer)
you need to unbox an Integer
to an int
manually. In the other words, each accept(Integer)
does 2 additional operations for boxing/unboxing. It needs to improve the performance so it does some special checking in the algorithm library.
Another reason is reusing a piece of code. The body of OfInt#forEachRemaining(Consumer) is a good example of applying Adapter Design Pattern for reusing OfInt#forEachRenaming(IntConsumer).
default void forEachRemaining(Consumer<? super Integer> action) { if (action instanceof IntConsumer) { // action's implementation is an example of Class Adapter Design Pattern // | forEachRemaining((IntConsumer) action); } else { // method reference expression is an example of Object Adapter Design Pattern // | forEachRemaining((IntConsumer) action::accept); } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With