slartidan
slartidan

Reputation: 21586

How to change the type of a function reference?

Java8 lets me use a method declaration as implementation of any of my interfaces, as long as it only contains one method.

However once it is defined, the type cannot be changed.

My code:

import java.util.function.IntConsumer;

public class A {

    interface MyConsumer {
        void doSomething(int i);
    }

    public static void main(String[] args) {
        IntConsumer i = A::consume;
        MyConsumer i2 = A::consume;

        IntConsumer i3 = (IntConsumer) i2; // ClassCastException
        MyConsumer i4 = (MyConsumer) i; // ClassCastException

        IntConsumer i5 = i2; // does not compile
        MyConsumer i6 = i; // does not compile

        IntConsumer i7 = i2::doSomething; // workaround, as suggested by @Eran http://stackoverflow.com/a/35846001/476791
        MyConsumer i8 = i::accept;

        Object i9 = A::consume; // does not compile
        Object i10 = (IntConsumer) A::consume; // works
        Object i11 = (MyConsumer) A::consume; // works
        Object i12 = (MyConsumer) (IntConsumer) A::consume; // does not work
    }

    static void consume(int i) {
        System.out.println(i);
    }
}

Note: if you inline some of the variables, then some of the non-working examples suddenly start to work.

My thoughts in detail:

  1. if IntConsumer i = A::consume is valid, then A::consume has to implement IntConsumer
  2. if MyConsumer i2 = A::consume is valid, then A::consume has to implement MyConsumer
  3. if A::consume implements both, IntConsumer and MyConsumer, then casting should be possible

Is there another way to "cast" or "change" the type of the consumer afterwards?

Upvotes: 3

Views: 114

Answers (2)

Louis Wasserman
Louis Wasserman

Reputation: 198163

A::consume has no inherent type. When it appears in a context in which it is being used or assigned to a functional interface type, the compiler will create an object of that type implemented using A::consume.

Once you have that, you have an object of that specific functional interface type. It's not a method; it's a perfectly normal object. Java objects can't pretend to be other types their real type doesn't implement or extend.

So once you have an IntConsumer, you can't cast it to a MyConsumer. You can use it to get a method reference that can then become a new MyConsumer, but that one object will always be just an IntConsumer, nothing more.

Upvotes: 1

Peter Lawrey
Peter Lawrey

Reputation: 533570

Is there another way to "cast" or "change" the type of the consumer afterwards?

You can't change the actual type of an object(1). You can cast a primitive, and you can cast a reference to another type suitable for the object referenced.

What you can do is wrap the instance with the type you want.

MyConsumer mc = ((IntConsumer) A::consume)::accept;

(1) you can hack the class pointer in the object header, but this is highly unlikely to be a good idea.

Upvotes: 3

Related Questions