Это связано с моим ответом на "несовместимые потоки типы" . Я не знаю, почему то, что я предложил, и Holger справедливо нажал на меня. Но даже у него нет четкого объяснения, почему он работает. Итак, позвольте задать его как собственный вопрос:
Следующий код не компилируется в javac
(для ссылок на ideone ниже это sun-jdk-1.8.0_51
, за http://ideone.com/faq):
public <T> Object with(Stream<Predicate<? super T>> predicates) {
return predicates.reduce(Predicate::or);
}
И это правильно: объединение двух предикатов из этого потока похоже на запись:
Predicate<? super T> a = null;
Predicate<? super T> b = null;
a.or(b); // Compiler error!
Однако он компилируется в intellij, хотя с предупреждением о необработанном типе в методе Predicate::or
. По-видимому, он также будет компилироваться в затмении (в соответствии с исходным вопросом).
Но этот код делает:
public <T> Object with(Stream<Predicate<? super T>> predicates) {
return predicates.map(a -> a).reduce(Predicate::or);
// ^----------^ Added
}
Несмотря на то, что я решил попробовать это, мне не совсем понятно, почему это сработает. Мое волнообразное объяснение состоит в том, что .map(a -> a)
действует как "литье" и дает алгоритму вывода типа немного более гибкое, чтобы выбрать тип, который позволяет применять reduce
. Но я точно не знаю, что это за тип.
Обратите внимание, что это не эквивалентно использованию .map(Function.identity())
, поскольку это ограничение возвращает тип ввода. Ideone demo
Может кто-нибудь объяснить, почему это работает со ссылкой на спецификацию языка, или если, как было предложено Хольгером, это ошибка компилятора?
Немного подробнее:
Возвращаемый тип метода можно сделать более конкретным; Я опустил его выше, чтобы неприятные дженерики возвращаемого типа не мешали:
public <T> Optional<? extends Predicate<? super T>> with(
Stream<Predicate<? super T>> predicates) {
return predicates.map(a -> a).reduce(Predicate::or);
}
Это результат компиляции с -XDverboseResolution=all
. Не совсем уверен, что это наиболее релевантный вывод, который я могу опубликовать для отладки вывода типа; пожалуйста, сообщите, есть ли что-то лучше:
Interesting.java:5: Note: resolving method <init> in type Object to candidate 0
class Interesting {
^
phase: BASIC
with actuals: no arguments
with type-args: no arguments
candidates:
#0 applicable method found: Object()
Interesting.java:7: Note: resolving method map in type Stream to candidate 0
return predicates.map(a -> a).reduce(Predicate::or);
^
phase: BASIC
with actuals: <none>
with type-args: no arguments
candidates:
#0 applicable method found: <R>map(Function<? super T#1,? extends R>)
(partially instantiated to: (Function<? super Predicate<? super T#2>,? extends Object>)Stream<Object>)
where R,T#1,T#2 are type-variables:
R extends Object declared in method <R>map(Function<? super T#1,? extends R>)
T#1 extends Object declared in interface Stream
T#2 extends Object declared in method <T#2>with(Stream<Predicate<? super T#2>>)
Interesting.java:7: Note: Deferred instantiation of method <R>map(Function<? super T#1,? extends R>)
return predicates.map(a -> a).reduce(Predicate::or);
^
instantiated signature: (Function<? super Predicate<? super T#2>,? extends Predicate<CAP#1>>)Stream<Predicate<CAP#1>>
target-type: <none>
where R,T#1,T#2 are type-variables:
R extends Object declared in method <R>map(Function<? super T#1,? extends R>)
T#1 extends Object declared in interface Stream
T#2 extends Object declared in method <T#2>with(Stream<Predicate<? super T#2>>)
where CAP#1 is a fresh type-variable:
CAP#1 extends Object super: T#2 from capture of ? super T#2
Interesting.java:7: Note: resolving method reduce in type Stream to candidate 1
return predicates.map(a -> a).reduce(Predicate::or);
^
phase: BASIC
with actuals: <none>
with type-args: no arguments
candidates:
#0 not applicable method found: <U>reduce(U,BiFunction<U,? super T,U>,BinaryOperator<U>)
(cannot infer type-variable(s) U
(actual and formal argument lists differ in length))
#1 applicable method found: reduce(BinaryOperator<T>)
#2 not applicable method found: reduce(T,BinaryOperator<T>)
(actual and formal argument lists differ in length)
where U,T are type-variables:
U extends Object declared in method <U>reduce(U,BiFunction<U,? super T,U>,BinaryOperator<U>)
T extends Object declared in interface Stream
Interesting.java:7: Note: resolving method metafactory in type LambdaMetafactory to candidate 0
return predicates.map(a -> a).reduce(Predicate::or);
^
phase: BASIC
with actuals: Lookup,String,MethodType,MethodType,MethodHandle,MethodType
with type-args: no arguments
candidates:
#0 applicable method found: metafactory(Lookup,String,MethodType,MethodType,MethodHandle,MethodType)
Interesting.java:7: Note: resolving method metafactory in type LambdaMetafactory to candidate 0
return predicates.map(a -> a).reduce(Predicate::or);
^
phase: BASIC
with actuals: Lookup,String,MethodType,MethodType,MethodHandle,MethodType
with type-args: no arguments
candidates:
#0 applicable method found: metafactory(Lookup,String,MethodType,MethodType,MethodHandle,MethodType)