Reputation: 7845
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:
updateManager.doUpdateForEach(it, DatabaseOperator::updateInfo);
UpdateManager#doUpdateForEach
method expects a RecordIterator
(ok, makes sense), and a FunctionalStuff
FunctionalStuff
has a single method (obviously), that receives 2 paramsdoUpdateForEach
is a method reference (DatabaseOperator::updateInfo
)DatabaseOperator::updateInfo
method receives a single argumenthow 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
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
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)
, wherea
andb
are arbitrary names used to better describe this example. The method reference would invoke the methoda.compareToIgnoreCase(b)
.
Upvotes: 2
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
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