Я пытаюсь написать некоторый код, который получает различные реализации классов в пакете java.util.function.*
, но я продолжаю работать с определенной ошибкой компилятора для определенного синтаксиса, который я действительно хотел бы обойти (аргументы явного типа, такие как obj.<Type>foo(/*...*/)
работают, но им не хватает благодати).
Короче говоря, я хотел бы иметь возможность всегда использовать ссылки на методы там, где это возможно, и по какой-то причине я не понимаю, почему компилятор не может понять ссылки, описанные ниже.
Предположим, что следующие два класса (реализация не имеет значения):
Некоторый класс сущности с методами получения/установки:
class Thing {
public List<String> getThings() {
return null;
}
public void setThings(List<String> things) {
// ...
}
}
Некоторый другой класс, который выполняет действия над экземплярами этого класса сущностей:
class FooBar<A> {
public <B> void foo(Function<A, List<B>> f) {
// ...
}
public <B> void bar(BiConsumer<A, List<B>> f) {
// ...
}
public <B> void wuz(Function<A, List<B>> x, BiConsumer<A, List<B>> y) {
// ...
}
}
При выполнении вызовов этих методов компилятор выдает мне разные ошибки:
// create an instance of the handler class
FooBar<Thing> fb = new FooBar<>();
Бывший. 1) Вызов метода, который ожидает функцию, работает нормально, ошибок компиляции нет:
fb.foo(Thing::getThings);
Бывший. 2) Тем не менее, вызов метода, который ожидает биконсумента, дает мне следующее:
fb.bar(Thing::setThings);
// The type Thing does not define setThings(Thing, List<B>) that is applicable here
Бывший. 3) Как и ожидалось, явное указание типа работает нормально, без ошибок компиляции:
fb.<String>bar(Thing::setThings);
Бывший. 4) Тем не менее, когда я пишу лямбда от руки, это дает мне другую ошибку компиляции (хотя указание типов в лямбда-аргументе работает нормально):
fb.bar((thing, things) -> thing.setThings(things));
// The method setThings(List<String>) in the type Thing is not applicable for the arguments (List<Object>)
Бывший. 5) И при вызове метода, который ожидает оба, я получаю другую ошибку компиляции для каждого аргумента:
fb.wuz(
Thing::getThings,
// The type of getThings() from the type Thing is List<String>, this is incompatible with the descriptor's return type: List<B>
Thing::setThings
// The type Thing does not define setThings(Thing, List<B>) that is applicable here
);
Бывший. 6) Опять же, как и ожидалось, явное указание типа снова работает нормально, без ошибок компиляции:
fb.<String>wuz(Thing::getThings, Thing::setThings);
Бывший. 7) И это то, что меня больше всего озадачивает: для метода, который ожидает и функцию, и биконсумер, записывая только биконсумер (без типов) и используя ссылки на методы для функции, работает нормально, нет ошибок компиляции (?!):
fb.wuz(Thing::getThings, (thing, things) -> thing.setThings(things));
Чего я не понимаю, так это того, что, по-видимому, компилятор обрабатывает явные лямбда-выражения и ссылки на методы по-разному при определении стирания типа/типа во время выполнения в разных сценариях, а именно:
- Функциональный интерфейс, который получает аргументы и возвращает значение
- Функциональный интерфейс, который получает аргументы, но не возвращает значения
- Метод с аргументами для типов 1. и 2.
Кажется, это происходит только тогда, когда тип функционального интерфейса сам по себе является универсальным типом (в данном примере это список), что наводит меня на мысль, что это имеет какое-то отношение к стиранию типа, но я в недоумении относительно отвечать.
Я хотел бы иметь возможность писать...
fb.wuz(Thing::getThings, Thing::setThings);
... без явного типа или длинного лямбда-выражения.
Если есть способ реорганизовать методы в FooBar
для поддержки этого, мне бы очень хотелось знать.
Конечно, если кто-то может объяснить эти различные варианты поведения компилятора, я тоже буду очень признателен! :-)
ИЗМЕНИТЬ
Мне особенно интересно узнать, почему примеры № 1 и № 7 работают, но почему пример № 4 сам по себе не работает (а затем, почему пример # 2 тоже не работает).
class FooBar<A, B>
; удалить ‹B› из сигнатур методов;FooBar<Thing, String> fb = new FooBar<>();
- person Javi Mollá   schedule 10.03.2015jdk
вы используете? В моем случае все вызовы, кроме одного, компилируются без ошибок. Я загружаю самую последнюю версию прямо сейчас, чтобы проверить результаты. - person Holger   schedule 23.03.2015