Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting Java functional interfaces

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)

like image 562
Andrii Abramov Avatar asked May 31 '17 15:05

Andrii Abramov


People also ask

What are the 4 types of functional interfaces?

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.

Can we instantiate functional interface in Java?

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.

Can functional interface have concrete methods?

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.

Can we write lambda without functional interface?

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.


1 Answers

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);     } } 
like image 161
holi-java Avatar answered Sep 20 '22 20:09

holi-java