Неожиданное преобразование функционального интерфейса Java - PullRequest
0 голосов
/ 27 декабря 2018

У меня есть следующий фрагмент кода, который использует функциональные интерфейсы Java, который компилируется, но неясно, почему он компилируется:

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 {

}

Итак, моя путаница связана с методом main:

  • последняя строка основного метода: updateManager.doUpdateForEach(it, DatabaseOperator::updateInfo);
  • метод UpdateManager#doUpdateForEach ожидает RecordIterator (хорошо, имеет смысл) и FunctionalStuff
  • FunctionalStuff имеет единственный метод (очевидно), который получает 2 параметра
  • Второй аргумент doUpdateForEach является ссылкой на метод (DatabaseOperator::updateInfo)
  • .DatabaseOperator::updateInfo метод получает один аргумент

как это компилируется?Как ссылка на метод DatabaseOperator::updateInfo преобразуется в функциональный интерфейс?Я что-то упускаю из виду?Или это какой-то угловой случай функциональных интерфейсов?

Ответы [ 4 ]

0 голосов
/ 27 декабря 2018

Существует 4 различных типа ссылки на метод, и вы используете Ссылка на метод экземпляра объекта определенного типа

Теперь на первый взгляд это похоже на static method reference, чтоClass::staticType, но имеет следующую разницу:

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

Итак, в вашем случае метод DatabaseOperator#updateInfo присутствует в классе DatabaseOperator, который совпадает с типом DatabaseOperator первого аргументаВ методе execute внутри функционального интерфейса число аргументов в методе на единицу меньше, поскольку в качестве первого аргумента берется ссылка this.

Если вы измените DatabaseOperator # updateInfo на два аргумента,Компилятор выдаст ошибку «1017».Таким образом, вы можете либо создать метод, который будет использоваться как ссылочный, статический, либо использовать new DatabaseOperator()#updateInfo, который является двумя другими типами ссылок на метод в java.

0 голосов
/ 27 декабря 2018

Как ссылка метода DatabaseOperator::updateInfo преобразуется в функциональный интерфейс?

Эффективное лямбда-представление ссылки на метод:

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

, чтодальнейшее представление анонимного класса:

new FunctionalStuff<MyRecord>() {
    @Override
    public void execute(DatabaseOperator databaseOperator, MyRecord r) throws Exception {
        databaseOperator.updateInfo(r);
    }
});
0 голосов
/ 27 декабря 2018

Прежде всего, FunctionalStuff<T> определяется следующим образом:

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

Ссылка на метод DatabaseOperator::updateInfo преобразуется в экземпляр типа FunctionalStuff<MyRecord> (я оставил фактические типы для очистки,но они могут быть опущены):

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

Или, если вы хотите использовать его как анонимный класс:

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

См. учебник со следующим примером:

Ссылка на метод экземпляра произвольного объекта определенного типа

Ниже приведен пример ссылки на метод экземпляра произвольного объектаобъект определенного типа:

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

Эквивалентное лямбда-выражение для ссылки на метод String::compareToIgnoreCase будет иметь список формальных параметров (String a, String b), где a и b - произвольные имена, используемые для улучшенияопишите этот пример.Ссылка на метод будет вызывать метод a.compareToIgnoreCase(b).

0 голосов
/ 27 декабря 2018

Это не то, как на самом деле работают лямбды, но по сути вы могли бы видеть

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

как

DatabaseOperator referencedMethodOwner = instanceGivenAsMethodExpression;
updateManager.doUpdateForEach(it, 

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