Скажем, у меня есть метод, который принимает java.util.function.Predicate и return CompletableFuture:
public <R> CompletableFuture<R> call(Predicate<R> request) {
return new CompletableFuture<>();
}
Если я вызову этот метод, используя анонимный класс, например:
Integer a = call(new Predicate<Integer>() {
@Override
public boolean test(Integer o) {
return false;
}
}).join();
Это работает, потому что я должен явно объявить тип Предиката. Однако, если я использую выражение лямбда вместо анонимного класса следующим образом:
Integer a = call(o -> false).join();
Он не компилируется, потому что Java думает, что это Predicate<Object>
и возвращает такое сообщение:
Error:(126, 42) java: incompatible types: java.lang.Object cannot be converted to java.lang.Integer
Есть несколько обходных решений, которые я нашел. Я могу создать переменную CompletableFuture
явно вместо цепочки или добавить лишний дополнительный аргумент Class<R>
который сообщает Java, какой тип мы хотим получить или заставить тип в лямбда-выражении.
Однако я удивляюсь, почему Java выбирает Object
вместо Integer
в первом примере лямбда, он уже знает, какой тип я хочу получить, поэтому компилятор может использовать наиболее специфический тип вместо Object
потому что все три обходных пути кажутся мне уродливыми.
Существуют ограничения на вывод типа Java 8, и он не смотрит на тип переменной, к которой вы назначили результат. Вместо этого он указывает тип Object
для параметра type.
Вы можете исправить это, явно указав тип аргумента o
в лямбда:
Integer a = call((Integer o) -> false).join();
ConsistentHashRing remoteRing = ctx.ask(oneMemberOfCluster, (RingMap service, OperationContext<ConsistentHashRing> ctx1) -> ctx1.reply(service.getRing())).join();
receiver.<R>call(...)
в тех случаях, когда это будет менее навязчивым.
Другие люди ответили, как заставить лямбду правильного типа. Тем не менее, я бы сказал, что Predicate<Object>
не является "неправильным" для этого предиката, и вам должно быть разрешено использовать его - у вас есть предикат для всех объектов - он должен работать на всех объектах, включая Integer
s; почему вам нужно сделать более ограниченный предикат для Integer
? Вы не должны.
Я бы сказал, что настоящая проблема - это подпись вашего метода call
. Это слишком строго. Predicate
- это потребитель своего параметра типа (у него есть только метод, который принимает тип типа своего типа и возвращает boolean
); поэтому, согласно правилу PECS (продюсер extends
, потребитель super
), вы всегда должны использовать его с super
ограниченным шаблоном.
Поэтому вы должны объявить свой метод следующим образом:
public <R> CompletableFuture<R> call(Predicate<? super R> request)
Независимо от того, какой код вы использовали в этом методе, он все равно будет компилироваться после изменения привязки к super
, потому что Predicate
является потребителем. И теперь вам не нужно заставлять его быть Predicate<Integer>
- вы можете использовать Predicate<Object>
и по-прежнему возвращать CompletableFuture<Integer>
.