pedrorijo91
pedrorijo91

Reputation: 7845

Unexpected Java Functional Interface conversion

I have the following piece of code, that uses java Functional Interfaces, that compiles, but it's not clear why does it compile:

public class App {

    public static void main(String[] args) throws Exception {

        final RecordIterator it = new RecordIterator<MyRecord>();

        final UpdateManager updateManager = new UpdateManager();
        updateManager.doUpdateForEach(it, DatabaseOperator::updateInfo);

    }
}

class UpdateManager {

    public void doUpdateForEach(final RecordIterator recordIterator,
                                final FunctionalStuff<MyRecord> updateAction) throws Exception {

        updateAction.execute(new DatabaseOperator(), new MyRecord());

    }

}

class RecordIterator<E> {

}

@FunctionalInterface
interface FunctionalStuff<T> {

    void execute(final DatabaseOperator database, final T iterator) throws Exception;

}

class DatabaseOperator {

    public void updateInfo(final MyRecord r) {

    }

}

class MyRecord {

}

So, my confusion is inside of the main method:

how does this compiles? How is the DatabaseOperator::updateInfo method reference converted into the functional interface? Am I missing something obvious? Or is some corner case of functional interfaces?

Upvotes: 11

Views: 1120

Answers (4)

Naman
Naman

Reputation: 31878

How is the DatabaseOperator::updateInfo method reference converted into the functional interface?

The effective lambda representation of your method reference is :

updateManager.doUpdateForEach(it, (databaseOperator, r) -> databaseOperator.updateInfo(r));

which is further representation of anonymous class:

new FunctionalStuff<MyRecord>() {
    @Override
    public void execute(DatabaseOperator databaseOperator, MyRecord r) throws Exception {
        databaseOperator.updateInfo(r);
    }
});

Upvotes: 8

Dorian Gray
Dorian Gray

Reputation: 2981

First of all, FunctionalStuff<T> is defined like that:

@FunctionalInterface
interface FunctionalStuff<T> {
    void execute(final DatabaseOperator database, final T iterator) throws Exception;
}

The method reference DatabaseOperator::updateInfo is converted to an instance of FunctionalStuff<MyRecord> like that (I left the actual types for clearification, but they can be omitted):

FunctionalStuff<MyRecord> func = (DatabaseOperator database, MyRecord r) -> database.updateInfo(r);

Or if you want to use it as an anonymous class:

FunctionalStuff<MyRecord> func = new FunctionalStuff<MyRecord>() {
    void execute(final DatabaseOperator database, final MyRecord r) {
        database.updateInfo(r);
    }
}

See the tutorial with the following example:

Reference to an instance method of an arbitrary object of a particular type

The following is an example of a reference to an instance method of an arbitrary object of a particular type:

String[] stringArray = { "Barbara", "James", "Mary", "John",
    "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);

The equivalent lambda expression for the method reference String::compareToIgnoreCase would have the formal parameter list (String a, String b), where a and b are arbitrary names used to better describe this example. The method reference would invoke the method a.compareToIgnoreCase(b).

Upvotes: 2

YK S
YK S

Reputation: 3440

There are 4 different types of method reference and you are using Instance method reference of an object of a particular type

Now at first sight it is similar to static method reference which is Class::staticType but it has the following difference:

- the method should be present in the class same as type of first argument in functional interface
- The method used should have one less argument as opposed to number of arguments in the method declared in functional interface as the **this** reference is taken as first argument.

So in your case the method DatabaseOperator#updateInfo is present in the DatabaseOperator class which is same as the type DatabaseOperator of the first argument of execute method inside the functional interface and the number of argument in the method is one less as the this reference is taken as the first argument.

If you change the DatabaseOperator#updateInfo to take two arguments then compiler will show an error saying Cannot make a static reference to the non-static method updateInfo from the type DatabaseOperator. So you can either make the method, to be used as reference, static or you have to use new DatabaseOperator()#updateInfo which are two other types of method reference in java.

Upvotes: 0

Antoniossss
Antoniossss

Reputation: 32517

This is not how lambdas actually work, but essentially you could see

updateManager.doUpdateForEach(it, DatabaseOperator::updateInfo);

as

DatabaseOperator referencedMethodOwner = instanceGivenAsMethodExpression;
updateManager.doUpdateForEach(it, 

    new FunctionalStuff{
        void execute(final T iterator) throws Exception{
            referencedMethodOwner.updateInfo(iterator)
       }
    });

Upvotes: -3

Related Questions