Java API de colecciones: ¿por qué no se puede modificar [Listado | Set | Mapa] no manifiestamente visibles clases?

StackOverflow https://stackoverflow.com/questions/4296619

Pregunta

Collections.unmodifiableList(...) devuelve una nueva instancia de un clase interna estática UnmodifiableList. Otras clases de colecciones no modificables se construyen misma manera.

fueron estas clases pública, uno tenía dos ventajas:

  • capacidad de indicar un valor de retorno específica más (como UnmodifiableList), por lo que un usuario de la API no vendría a la idea de modificar esa colección;
  • capacidad de comprobar en tiempo de ejecución si un List es instanceof UnmodifiableList.

Por lo tanto, estaban allí ninguna ventaja no para hacer esas clases público?

Editar . No se presentaron argumentos convincentes, sin duda, así que elegir la respuesta más upvoted

¿Fue útil?

Solución

Hay las dos ventajas están ahí, pero no son tan útiles. Los principales problemas siguen siendo los mismos: UnmodifiableList todavía es una lista y así todos los emisores están disponibles y las colecciones subyacentes todavía son modificables. Hacer público UnmodifiableList clase se sumaría a la ilusión de ser inmodificable.

La forma más agradable sería que el compilador de ayuda, pero para que las jerarquías de clases colección tendrían que cambiado mucho. Por ejemplo, la API colección de Scala es mucho más avanzado en ese sentido.

Una desventaja sería la introducción de al menos tres clases / interfaces adicionales en el API. Debido a ellos no ser tan útil, creo que lo que les deja fuera de la API es una opción buena.

Otros consejos

En lo personal estoy totalmente de acuerdo con usted. En el núcleo del problema es el hecho de que los genéricos de Java no son covariantes, los cuales, a su vez, se debe a que las colecciones de Java son mutables.

No es posible para el sistema de tipo de Java para codificar un tipo que parece tener mutators es realidad inmutable . Imagínese si tuviéramos que empezar a diseñar alguna solución:

interface Immutable //marker for immutability

interface ImmutableMap<K, V> extends Map<K, V>, Immutable

Pero entonces ImmutableMap es una subclase de Map, y por lo tanto Map es asignable a partir ImmutableMap por lo que cualquier método que devuelve un mapa tales inmutable:

public ImmutableMap<K, V> foo();

puede ser asignada a un mapa y por lo tanto puede ser mutado en tiempo de compilación:

Map<K, V> m = foo();
m.put(k, v); //oh dear

Por lo tanto, se puede ver que la adición de este tipo no ha hecho nos ha impedido hacer nada malo. Creo que por esta razón se hizo justicia que no tiene suficiente para ofrecer.


Un lenguaje como Scala tiene la declaración de sitio anotaciones de varianza. Es decir, se puede especificar un tipo como covariante (y por tanto inmutables) como Mapa de Scala es (en realidad es covariante en su parámetro V). De ahí su API puede declarar si su tipo de retorno es mutable o inmutable.

Como otro lado, Scala permite declarar tipos de intersección de modo que usted ni siquiera necesita para crear la interfaz ImmutableXYZ como una entidad separada, que podría indicar un procedimiento de retorno:

def foo : XYZ with Immutable

Pero Scala tiene entonces un sistema de tipo adecuado, mientras que Java no

Si es importante para que usted compruebe si la lista se creó con Collections.unmodifiableList entonces se puede crear una instancia y pedir la clase. Ahora se puede comparar esta clase con la clase de cualquier lista.

private static Class UNMODIFIABLE_LIST_CLASS = 
    Collections.unmodifiableList( new ArrayList() ).getClass();
...
if( UNMODIFIABLE_LIST_CLASS == listToTest.getClass() ){
    ...
} 

La respuesta a la razón por la es bastante simple: en el momento, en 1998, era un diseño eficiente flanky bits. La gente pensaba de él no fue al parecer una prioridad. Pero no había ningún verdadero pensamiento profundo, al respecto.

Si desea utilizar un mecanismo de este tipo, utilice Guava ImmutableList / Set / Mapa /...

Ellos son explícitamente inmutable y una práctica buena cuando se utiliza esa biblioteca no es para devolver una lista, por ejemplo, sino una ImmutableList. Así sabrá que una Lista / Set / Mapa / ... es inmutable.

Ejemplo:

private final ImmutableList constants = ...;
public final ImmutableList<String> getConstants() {
  return constants;
}

Sobre el diseño mismo de UnmodifiableXxx, uno podría haber hecho lo siguiente:

public static final class UnmodifiableXxx implements Xxx { // don't allow extend
  // static if inside Collections
  UnmodifiableXxx (Xxx backend) { // don't allow direct instanciation
    ...
  }
  ...
}
  
      
  • capacidad de indicar un valor de retorno específica más (como UnmodifiableList), por lo que un usuario de la API no vendría a la idea de modificar esa colección;
  •   

En una API adecuada, esto ya debería estar documentada en el Javadoc del método de devolución de la lista no se puede modificar.

  
      
  • capacidad de comprobar en tiempo de ejecución si un List es instanceof UnmodifiableList.
  •   

Tal necesidad indica que los mentiras reales de problemas en otro lugar. Es un defecto en el diseño de código. Pregúntese, ha tenido alguna vez la necesidad de comprobar si un List es una instancia de ArrayList o LinkedList? Ya se trate de un ArrayList, LinkedList o UnmodifiableList es claramente una decisión que se va a realizar durante el tiempo de escribir código, no durante el tiempo de ejecución de código. Si los problemas persisten, ya que está intentando modificar una UnmodifiableList (para el cual el desarrollador API puede tener muy buenas reasions que debe ser ya documentadas), entonces es más bien su propia culpa, no es un fallo de tiempo de ejecución.

Todos con todo, no tiene sentido. El Collections#unmodifiableXXX(), synchronizedXXX() y checkedXXX() en modo alguno no representan implementaciones concretas. Todos ellos son simplemente decoradores que se puede aplicar independientemente del hormigón subyacente aplicación.

Supongamos UnmodifiableList era una clase pública. Sospecho que sería calmar a los programadores en una falsa sensación de seguridad. Recuerde, UnmodifiableList es una vista de un modificable List. Esto significa que el contenido de un UnmodifiableList todavía puede cambiar a través de los cambios realizados en su List subyacente. Un programador ingenuo no puede entender este matiz y puede esperar que las instancias de UnmodifiableList que son inmutables.

Creo que la respuesta se debe a la forma adecuada método sabe acerca de los medicamentos genéricos utilizados y no requiere programación adicional para pasar esta información a través, mientras que el formulario de la clase requeriría más perder el tiempo. La forma método para unmodifiableMap tiene dos argumentos genéricos flotantes, que se asigna a ambos los argumentos genéricos del tipo de retorno y del argumento pasado.

public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m) {
    return new UnmodifiableMap<K,V>(m);
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top