el siguiente método se compila sin problemas:
static Stream<Optional<? extends Number>> getNumbers(Stream<Number> numbers) { return numbers.map(Optional::of); }sin embargo, si le agrego un filtrado simple como este:
static Stream<Optional<? extends Number>> getNumbers2(Stream<Number> numbers) { return numbers.map(Optional::of).filter(number -> true); }genera el siguiente error:
tipos incompatibles:
java.util.stream.Stream<java.util.Optional<java.lang.Number>> no se puede convertir a
java.util.stream.Stream<java.util.Opcional<? extiende java.lang.Number>>
probado en openJdk-11 y openJdk-17.
Esperaría que ambos hicieran lo mismo (o ambos compilan bien o ambos generan el mismo error de compilación), así que estoy realmente desconcertado por esto: ¿cuál es la regla general aquí que explica por qué el primer método compila bien pero el segundo ¿no? ¡Gracias!
Compatibilidad con el tipo de retorno Stream<Optional<? extends Number>> en el primer caso no se obtiene en virtud de numbers.map(Optional::of) que devuelve un Stream<Optional<? extends Number>> por sí mismo; es el compilador el que infiere el tipo de retorno de numbers.map(...) debido a que es un método genérico:
<R> Stream<R> map(Function<? super T, ? extends R> mapper); mientras que Stream.filter() no lo es:
Stream<T> filter(Predicate<? super T> predicate); Por lo tanto, en el primer caso, el compilador puede tener en cuenta el contexto de la instrucción de retorno (tipo de getNumbers ) al inferir el tipo de numbers.map(...) .
El compilador no puede hacer lo mismo para los numbers.map(...) en el segundo caso, ya que hay llamadas encadenadas posteriores, que pueden cambiar aún más el tipo, por lo que sería muy difícil adivinar cuál debería ser la inferencia correcta en esta etapa. . Como resultado, se asume el tipo posible más específico para numbers.map(...) ( Stream<Optional<Number>> ) y se continúa con filter(...) .
Como un ejemplo diferente para ilustrar eso, descubra por qué ambos compilan ( List.of() es el mismo código, después de todo):
static List<String> stringList() { return List.of(); } static List<Integer> intList() { return List.of(); }Ahora, ¿por qué falla esto?
static List<String> stringList() { return List.of().subList(0, 0); } Esto se debe a que List.subList(...) no infiere el tipo E de la lista devuelta en contexto (es decir, el método no es genérico), lleva el tipo E de la instancia de List , que, con List.of() en ese caso obtiene el valor predeterminado es Object (sí, cuando tiene return List.of(); , se activa la inferencia de tipo de retorno, lo que obliga al compilador a darse cuenta de que la intención es hacer que E coincida con String , el argumento de tipo en el tipo de retorno del método). Tenga en cuenta que esto se vuelve más complejo que eso, hay esquinas donde la inferencia no funciona como se desea/espera.
Respuesta corta : return numbers.map(Optional::of) aprovecha la inferencia de tipo ya que map() es genérico y filter() no, esperando que se transporte la E de Stream<E> . Y con numbers.map(Optional::of) , E es Optional<Number> , no Optional<? extends Number> , y el filter lo lleva.