2010-03-22 11 views
12

Quiero tener objetos inmutables de Java como esto (muy simplificada):clase mutable como hijo de una clase inmutable

class Immutable { 

    protected String name; 

    public Immutable(String name) { 
     this.name = name; 
    } 

    public String getName() { 
     return name; 
    } 

} 

En algunos casos, el objeto no sólo debe ser legible, pero mutable, por lo que podría añadir mutabilidad través de la herencia:

public class Mutable extends Immutable { 

    public Mutable(String name) { 
     super(name); 
    } 

    public void setName(String name) { 
     super.name = name; 
    } 

} 

Si bien esto es técnicamente muy bien, me pregunto si se conforma con programación orientada a objetos y la herencia que mutable es también de tipo inmutable. Quiero evitar que el crimen de OOP arroje UnsupportedOperationException por objeto inmutable, como hace la API de colecciones de Java.

¿Qué opinas? ¿Alguna otra idea?

+1

Esto es exactamente lo que hace Objetivo C: '' NSMutableString' hereda de NSString', '' NSMutableArray' hereda de NSArray', etc. – Nefrubyr

Respuesta

14

Evite llamar al padre "Inmutable" porque se convierte en una mentira en la clase secundaria - si do desea hacer que una clase sea inmutable, también debe ser definitiva para evitar exactamente este problema.

Joda Time usa "ReadableXXX" para dar la idea de que "esta clase solo proporciona acceso de lectura, otras subclases pueden ser mutables". No estoy seguro si me gusta esa idea, pero no puedo decir que haya visto muchas alternativas.

Básicamente el problema es con la expresión de un negativo - Immutable describe lo que puede no do (mutar ella) y que no se pueden aplicar con sensatez en las subclases. (Incluso si los campos dentro de Immutable fueran finales, no detendría a una subclase que tenga sus propios campos mutables.)

3

Las clases inmutables deben ser final precisamente para evitar subtipos mutables.

Permitir que un subtipo de una clase inmutable rompa el contrato inmutable hace que sea inútil que la clase sea inmutable en primer lugar. Puede ser legal en el sentido de que Java le permite hacerlo (la inmutabilidad no se aplica en el idioma) pero dicha clase no es verdaderamente inmutable, siempre que pueda ser subclasificado.

Esta es la razón por la cual String es final.

+1

Considérese que necesita la clase base y subclases ImmutableCollection ImmutableList, ImmutableSet. ¿Cómo se puede hacer aquí la clase base final? El problema es que ningún compilador puede expresar todas las contracciones que pueden aparecer en su modelo. Por lo tanto, deberíamos esperar que el compilador verifique todo por nosotros. – Alexey

1

Me parece curioso su código.

Para implementar un comportamiento tan inmutable, preferiría haber confiado en una interfaz Immutable, proporcionando solo el método getter, mientras el objeto contenga ambos. De esta manera, las operaciones que dependen de los objetos inmutables habrían llamado a la interfaz, mientras que otras habrían llamado al objeto.

Y, si realmente no desea que sus objetos inmutables se conviertan en elementos mutables, puede usar proxies y todas las herramientas empresariales (aspectos, etc.). Pero generalmente, depender de la buena voluntad de otros desarrolladores es una manera dulce de hacerlos responsables de sus errores (como lanzar lo inmutable en mutable).

+1

Los apoderados pueden ser demandados para hacer que un objeto sea de solo lectura, pero no hacen que un objeto sea inmutable. La única forma de hacer que un objeto posiblemente mutable sea inmutable es separar cada parte del mismo que pueda ser mutable, en cuyo caso el objeto ya no es un proxy. – supercat

2

Su subclase es mala porque infringe el Liskov substitution principle. No lo hagas

+2

No, no es así. Las instancias de 'Inmutable' se pueden reemplazar por instancias de' Mutable' sin alterar el comportamiento del código. – emlai

+0

No entiendo por qué esta respuesta incorrecta todavía está aquí, sin ninguna edición. El código – uetoyo

+0

que requiere un objeto inmutable debería actualizarse para realizar copias defensivas. de lo contrario, podrían ocurrir cosas malas en ciertas situaciones. quizás eso técnicamente no sea LSP. – les2

4

Sugiero que debe tener una clase base heredable "ReadableFoo", una clase ImmutableFoo sellada derivada y otras clases MutableFoo derivadas. El código que no se preocupa si un Foo es mutable o no puede aceptar un ReadableFoo. El código que quiere un Foo que está garantizado que no cambia puede aceptar un ImmutableFoo. El código que puede necesitar cambiar un Foo puede aceptar un MutableFoo.

Tenga en cuenta que los constructores de ImmutableFoo y MutableFoo normalmente deberían aceptar un ReadableFoo. De esta forma, cualquier Foo será convertible a una versión mutable o inmutable.

Cuestiones relacionadas