web-dev-qa-db-fra.com

Conversion inattendue de l'interface fonctionnelle Java

J'ai le morceau de code suivant, qui utilise des interfaces fonctionnelles Java, qui compile, mais on ne sait pas pourquoi il 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 {

}

Donc, ma confusion est à l’intérieur de la méthode main:

  • la dernière ligne de la méthode principale est updateManager.doUpdateForEach(it, DatabaseOperator::updateInfo);
  • la méthode UpdateManager#doUpdateForEach attend un RecordIterator (ok, est logique), et un FunctionalStuff
  • FunctionalStuff a une seule méthode (évidemment), qui reçoit 2 paramètres
  • Le deuxième argument de doUpdateForEach est une référence de méthode (DatabaseOperator::updateInfo)
  • La méthode DatabaseOperator::updateInfo reçoit un seul argument

comment cela se compile-t-il? Comment la référence à la méthode DatabaseOperator::updateInfo est-elle convertie dans l'interface fonctionnelle? Est-ce que je manque quelque chose d'évident? Ou est-ce un cas de coin d'interfaces fonctionnelles? 

12
pedrorijo91

Comment la référence de la méthode DatabaseOperator::updateInfo est-elle convertie dans l'interface fonctionnelle?

La représentation lambda effective de votre référence de méthode est:

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

qui est une représentation supplémentaire de la classe anonyme:

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

Tout d’abord, FunctionalStuff<T> est défini comme ceci:

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

La référence à la méthode DatabaseOperator::updateInfo est convertie en une instance de FunctionalStuff<MyRecord> comme celle-ci (j'ai laissé les types réels pour la suppression, mais ils peuvent être omis):

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

Ou si vous voulez l'utiliser comme classe anonyme:

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

Voir le tutorial avec l'exemple suivant:

Référence à une méthode d'instance d'un objet arbitraire d'un type particulier

Voici un exemple de référence à une méthode d'instance d'un objet arbitraire d'un type particulier:

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

Expression lambda équivalente pour la référence de la méthode String::compareToIgnoreCase aurait la liste de paramètres formelle (String a, String b), où a et b sont des noms arbitraires utilisés pour mieux décrire cet exemple. La référence à la méthode invoquerait le méthode a.compareToIgnoreCase(b).

2
Dorian Gray

Il existe 4 types de référence de méthode différents et vous utilisez Référence de méthode d'instance d'un objet d'un type particulier

À première vue, il ressemble à static method reference qui est Class::staticType mais il présente la différence suivante:

- 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.

Donc, dans votre cas, la méthode DatabaseOperator#updateInfo est présente dans la classe DatabaseOperator qui est identique au type DatabaseOperator du premier argument de la méthode execute dans l'interface fonctionnelle et le nombre d'arguments dans la méthode est égal à un de moins, car la référence this est prise comme étant la valeur premier argument.

Si vous modifiez le paramètre DatabaseOperator # updateInfo pour qu'il prenne deux arguments, le compilateur affichera une erreur disant Cannot make a static reference to the non-static method updateInfo from the type DatabaseOperator. Vous pouvez donc créer la méthode, à utiliser comme référence, statique ou vous devez utiliser new DatabaseOperator()#updateInfo, qui sont deux autres types de référence de méthode en Java.

0
Yug Singh