streams sort procesamiento parte ejemplo datos con collection java java-8 java-stream comparable

procesamiento - ¿Por qué Stream.sorted no es seguro para el tipo en Java 8?



sort list java 8 (3)

¿Cómo implementarías eso? sorted es una operación intermedia (puede llamarse en cualquier lugar entre otras operaciones intermedias), lo que significa que puede comenzar con una secuencia que no es Comparable, pero llamada sorted en una que es Comparable :

Arrays.asList(new Foo(), new Foo()) .stream() .map(Foo::getName) // name is a String for example .sorted() .forEach(f -> {});

Lo que está proponiendo toma un argumento como entrada, pero Stream::sorted no, por lo que no puede hacer eso. La versión de sobrecarga acepta un Comparator , lo que significa que puede ordenar algo por una propiedad, pero aún así devolver la Stream<T> . Creo que esto es bastante fácil de entender si tratas de escribir tu esqueleto mínimo de una interfaz / implementación de Stream.

Esto es de la interfaz de Stream de la implementación de JDK 8 de Oracle:

public interface Stream<T> extends BaseStream<T, Stream<T>> { Stream<T> sorted(); }

y es muy fácil explotar esto en el tiempo de ejecución y no se generará ninguna advertencia en el momento de la compilación. Aquí hay un ejemplo:

class Foo { public static void main(String[] args) { Arrays.asList(new Foo(), new Foo()).stream().sorted().forEach(f -> {}); } }

que compilará bien pero lanzará una excepción en el tiempo de ejecución:

Exception in thread "main" java.lang.ClassCastException: Foo cannot be cast to java.lang.Comparable

¿Cuál podría ser la razón por la que el método sorted no se definió donde el compilador podría realmente detectar tales problemas? Tal vez estoy equivocado, pero no es así de simple:

interface Stream<T> { <C extends Comparable<T>> void sorted(C c); }

?

Obviamente, los chicos que implementan esto (que están a años luz de delante de mí en lo que respecta a la programación y la ingeniería se consideran) deben tener una muy buena razón por la que no puedo ver, pero ¿cuál es esa razón?


Esencialmente, está preguntando si hay una manera de decirle al compilador, " hey, este método requiere que el parámetro de tipo coincida con más límites específicos que los definidos a nivel de clase ". Esto no es posible en Java. Tal característica puede ser útil pero también esperaría confuso y / o complicado.

Tampoco hay manera de hacer que Stream.sorted() seguro en cuanto a cómo se implementan actualmente los genéricos; No si quieres evitar requerir un Comparator . Por ejemplo, estabas proponiendo algo como:

public interface Stream<T> { <C extends Comparable<? super T>> Stream<T> sorted(Class<C> clazz); } // other Stream methods omitted for brevity

Desafortunadamente, no hay garantía de que la Class<C> sea ​​asignable de la Class<T> . Considere la siguiente jerarquía:

public class Foo implements Comparable<Foo> { /* implementation */ } public class Bar extends Foo {} public class Qux extends Foo {}

Ahora puede tener una Stream de elementos de Bar , pero intente clasificarla como si fuera una Stream de elementos de Qux .

Stream<Bar> stream = barCollection.stream().sorted(Qux.class);

Dado que tanto Bar como Qux coinciden con Comparable<? super Foo> Comparable<? super Foo> no hay error de compilación y, por lo tanto, no se agrega seguridad de tipo. Además, la implicación de requerir un argumento de Class es que se usará para la conversión. En el tiempo de ejecución, esto, como se muestra arriba, puede resultar en ClassCastException s. Si la Class no se usa para lanzar, el argumento es completamente inútil; Incluso lo consideraría dañino.

El siguiente paso lógico es intentar y requerir C extender T así como Comparable<? super T> Comparable<? super T> . Por ejemplo:

<C extends T & Comparable<? super T>> Stream<T> sorted(Class<C> clazz);

Esto tampoco es posible en Java y produce un error de compilación: "el parámetro de tipo no puede ser seguido por otros límites". Incluso si esto fuera posible, no creo que solucione todo (si es que hay algo).

Algunas notas relacionadas.

Respecto a Stream.sorted(Comparator) : no es el Stream que hace que este método sea seguro, es el Comparator . El Comparator asegura que los elementos puedan ser comparados. Para ilustrar, la forma segura de escribir una Stream según el orden natural de los elementos es:

Stream<String> stream = stringCollection.stream().sorted(Comparator.naturalOrder());

Esto es de tipo seguro porque naturalOrder() requiere que su parámetro de tipo se extienda Comparable . Si el tipo genérico de la Stream no se extendía Comparable entonces los límites no coincidirían, lo que resultaría en un error de compilación. Pero, nuevamente, es el Comparator que requiere que los elementos sean Comparable *, mientras que al Stream simplemente no le importa.

Entonces, la pregunta es: ¿por qué los desarrolladores incluyeron un método sorted sin argumentos para Stream en primer lugar? Parece ser por razones históricas y se explica en una respuesta a otra pregunta de Holger.

* El Comparator requiere que los elementos sean Comparable en este caso. En general, un Comparator es obviamente capaz de manejar cualquier tipo para el que esté definido.


La documentation de Stream#sorted explica perfectamente:

Devuelve un flujo que consta de los elementos de este flujo, ordenados de acuerdo con el orden natural. Si los elementos de este flujo no son comparables, se puede lanzar una excepción java.lang.ClassCastException cuando se ejecuta la operación del terminal.

Está utilizando el método sobrecargado que no acepta argumentos (no el que acepta un Comparator ) y Foo no implementa Comparable .

Si está preguntando por qué el método no genera un error de compilación si el contenido de la Stream no implementa el Comparable , sería porque T no está obligado a extender la Comparable , y T no se puede cambiar sin una llamada al Stream#map ; parece ser solo un método conveniente, por lo que no es necesario proporcionar un Comparator explícito cuando los elementos ya implementan el Comparable .

Para que sea de tipo seguro, T tendría que extender Comparable , pero eso sería ridículo, ya que evitaría que una secuencia contenga cualquier objeto que no sea Comparable .