3

So I have a interface that extends BiConsumer.

@FunctionalInterface
public interface MyActionWithParameters<E, P> extends BiConsumer<Selection<E>, P> {
    @Override
    default MyActionWithParameters<E, P> andThen(BiConsumer<? super Selection<E>, ? super P> after) {
        var newAction = BiConsumer.super.andThen(after);
        return newAction::accept;
    }
}

This works.

But if I try to return (MyActionWithParameters<>) BiConsumer.super.andThen(after);, I get a class cast exception.

Why is that? Why can I cast newAction::accept (i.e. a new BiConsumer) but not the BiConsumer itself? I have a feeling the answer should be pretty obvious, but I just don't see it.

EDIT: full error

Caused by: java.lang.ClassCastException: class java.util.function.BiConsumer$$Lambda$2165/0x0000000840d10840 cannot be cast to class blah.blah.blah.MyFoo$MyActionWithParameters (java.util.function.BiConsumer$$Lambda$2165/0x0000000840d10840 is in module java.base of loader 'bootstrap'; blah.blah.blah.MyFoo$MyActionWithParameters is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @739fc679)
User1291
  • 7,664
  • 8
  • 51
  • 108
  • What purpose does this interface serve? It doesn't seem to give you anything over and above the superinterface – Andy Turner Jun 11 '20 at 09:45
  • @AndyTurner Well, somebody made the decision to introduce an extra interface "in case we ever need to extend the behaviour" and "to enforce semantic encapsulation". So far, we haven't needed to extend it, so I'm fairly certain it's over-engineered, but we're stuck with it, for now, and so I'm adding the `andThen` s.t. the type-checker accepts the results of the `myAction.andThen(...)` call. – User1291 Jun 11 '20 at 09:49

1 Answers1

4

The reason why casting does not work is because whatever BiConsumer.andThen returns, it does not implement MyActionWithParameters. How on earth is BiConsumer, a JDK interface, supposed to know about your own custom interface? :-)

But using a method reference works, because by using a method reference, the type of object that super.andThen returns, aka newAction, no longer matters. What matters is the method - accept. Does accept have a compatible signature to satisfy the functional interface of MyActionWithParameters? Yes it does, so it works.

Sweeper
  • 213,210
  • 22
  • 193
  • 313