UnsupportedOperationException en las interfaces de framework de colecciones java

12

Mirando a través del Java Collections Framework, he notado que algunas de las interfaces tienen el comentario (optional operation). Estos métodos permiten implementar clases a través de un UnsupportedOperationExceptionsi simplemente no quieren implementar ese método.

Un ejemplo de esto es el addAllmétodo en Set Interface.

Ahora, como se indica en esta serie de preguntas, las interfaces son un contrato definitorio de lo que el uso puede esperar.

Las interfaces son importantes porque separan lo que hace una clase de cómo lo hace. El contrato que define lo que un cliente puede esperar deja al desarrollador libre de implementarlo de la forma que elija, siempre y cuando mantenga el contrato.

y

Una interfaz es una descripción de las acciones que puede hacer un objeto ... por ejemplo, cuando se activa un interruptor de luz, la luz se enciende, no importa cómo, solo eso. En la programación orientada a objetos, una interfaz es una descripción de todas las funciones que debe tener un objeto para ser una "X".

y

Creo que el enfoque basado en la interfaz es significativamente mejor. Luego puede burlarse de sus dependencias muy bien, y todo está básicamente menos estrechamente acoplado.

¿Cuál es el punto de una interfaz?

¿Qué son las interfaces?

Interfaz + Extensión (mixin) vs Clase Base

Dado que el propósito de las interfaces es definir un contrato y hacer que sus dependencias se acoplen libremente, ¿no tiene algunos métodos arrojar una UnsupportedOperationExceptionespecie de derrota del propósito? Significa que ya no se me puede pasar ay Setsolo usarlo addAll. Más bien, tengo que saber qué implementación se Setme aprobó, para poder saber si puedo usarla addAllo no. Eso me parece bastante inútil.

Entonces, ¿de qué sirve UnsupportedOperationException? ¿Solo está compensando el código heredado y necesitan limpiar sus interfaces? ¿O tiene un propósito más sensorial que me estoy perdiendo?

Destino Espejado
fuente
No sé qué JRE está utilizando, pero mi versión 8 de Oracle no se define addAllen HashSet. Difiere a la implementación predeterminada en la AbstractCollectionque ciertamente no se lanza UnsupportedOperationException.
@Snowman Tienes razón. Leí mal los documentos. Editaré mi pregunta.
MirroredFate
1
Me gusta iniciar Eclipse y mirar el código fuente, rebotando alrededor de referencias y definiciones de código para asegurarme de que lo tengo correcto. Mientras el JRE esté vinculado a src.zipél, funciona muy bien. Es útil saber exactamente qué código se está ejecutando el JRE a veces y no diferir al JavaDoc, que puede ser un poco detallado.

Respuestas:

12

Mira las siguientes interfaces:

Todas estas interfaces declaran métodos de mutación como opcionales. Esto documenta implícitamente el hecho de que la clase Colecciones puede devolver implementaciones de esas interfaces que son inmutables: es decir, se garantiza que esas operaciones de mutación opcionales fallarán. Sin embargo, según el contrato en JavaDoc, todas las implementaciones de esas interfaces deben permitir las operaciones de lectura. Esto incluye las implementaciones "normales" como HashSety LinkedListasí como también los envoltorios inmutables Collections.

Contraste con las interfaces de cola:

Estas interfaces no especifican ninguna operación opcional: una cola, por definición, está diseñada para ofrecer y sondear elementos de manera FIFO. Una cola inmutable es tan útil como un automóvil sin ruedas.


Una idea común que surge repetidamente es tener una jerarquía de herencia que tenga objetos mutables e inmutables. Sin embargo, todos estos tienen inconvenientes. La complejidad enturbia las aguas sin resolver realmente el problema.

  • Un hipotético Setpodría tener las operaciones de lectura, y una subinterfaz MutableSetpodría tener las operaciones de escritura. Liskov nos dice que MutableSetluego se podría pasar a cualquier cosa que necesite a Set. Al principio, esto suena bien, pero considere un método que espera que el conjunto subyacente no se modifique mientras se lee: sería posible que dos hilos usen el mismo conjunto y violen la invariante del conjunto que no cambia. Esto podría causar un problema, por ejemplo, si un método lee un elemento del conjunto dos veces y está allí la primera vez, pero no la segunda.

  • Setpodría no tener implementaciones directas, sino tener MutableSety ImmutableSetcomo subinterfaces que luego se usan para implementar clases. Esto tiene el mismo problema que el anterior: en algún punto de la jerarquía, una interfaz tiene invariantes en conflicto. Uno dice "este conjunto debe ser mutable" y el otro dice "este conjunto no puede cambiar".

  • Podría haber dos jerarquías completamente separadas para estructuras de datos mutables e inmutables. Esto agrega una tonelada de complejidad adicional para lo que termina siendo muy poca ganancia. Esto también tiene la debilidad específica de los métodos que no se preocupan por la mutabilidad (por ejemplo, solo quiero iterar una lista) ahora deben admitir dos interfaces separadas. Dado que Java está estáticamente tipado, esto significa métodos adicionales para manejar ambas jerarquías de interfaz.

  • Podríamos tener una única interfaz y permitir que las implementaciones arrojen excepciones si un método no le es aplicable. Esta es la ruta que tomó Java, y tiene más sentido. El número de interfaces se mantiene al mínimo, y no hay invariantes de mutabilidad porque la interfaz documentada no garantiza la mutabilidad de ninguna manera . Si se requiere una invariabilidad de inmutabilidad, use los envoltorios en Collections. Si un método no necesita cambiar una colección, simplemente no lo cambie. El inconveniente es que un método no puede garantizar que una colección no cambie en otro subproceso si se le proporciona una colección desde el exterior, pero eso es una preocupación del método de llamada (o su método de llamada) de todos modos.


Lectura relacionada: ¿Por qué Java 8 no incluye colecciones inmutables?

Comunidad
fuente
1
Pero si los métodos son opcionales, ¿qué lugar tienen en la interfaz? ¿No debería haber una interfaz separada que contenga los métodos opcionales, por ejemplo MutableCollection?
MirroredFate
No. No hay forma de tener objetos mutables e inmutables en la misma jerarquía de manera significativa. Hubo una pregunta reciente que tenía un buen diagrama que mostraba la complejidad y una explicación de por qué es una mala idea, pero se elimina. Tal vez alguien más sabe de una pregunta para ayudar a explicar esto, no puedo encontrar nada. Pero actualizaré mi respuesta para explicar un poco.
Esa es una especie de declaración amplia sobre colas inmutables. Utilicé uno hace solo un par de días para resolver este problema .
Karl Bielefeldt
@Snowman Pero eso parece distinguir esos objetos mutables e inmutables como opuestos entre sí. Creo que los objetos inmutables son realmente objetos que no tienen la capacidad de mutar. Honestamente, la forma en que es ahora es más compleja y confusa, porque tienes que descubrir qué es una implementación mutable y qué no. Me parece que la única diferencia entre poner todos los métodos en una interfaz en lugar de dividir los métodos mutables es la claridad.
MirroredFate
@MirroredFate leyó mi edición más reciente.
2

Básicamente es YAGNI. Todas las colecciones concretas en la biblioteca estándar son mutables, implementan o heredan las operaciones opcionales. A ellos no les importan las colecciones inmutables de uso general y tampoco a la gran mayoría de los desarrolladores de Java. No van a crear una jerarquía de interfaz completa solo para colecciones inmutables, luego no incluirán ninguna implementación.

Por otro lado, hay algunos valores de propósito especial o colecciones "virtuales" que podrían ser muy útiles como inmutables, como un conjunto vacío y nCopies . Además, hay colecciones inmutables de terceros (como las de Scala), que pueden querer llamar al código Java existente, por lo que dejaron abierta la posibilidad de colecciones inmutables de la manera menos disruptiva.

Karl Bielefeldt
fuente
Ok, eso tiene sentido. Sin embargo, todavía parece que aborda el problema desde la dirección incorrecta. ¿Por qué no comenzar definiendo las interfaces de colección inmutable y luego definir las interfaces mutables para las implementaciones de colección mutable?
MirroredFate