una puede poo objeto modificador metodos metodo instanciar inmutable finales clases clase java immutability final

java - poo - se puede instanciar una clase final



¿Por qué la palabra clave final es necesaria para una clase inmutable? (7)

''final'', como sugiere el nombre de la palabra clave, significa que el atributo al que se adjunta la palabra clave final no se puede cambiar (en términos de valor) en otras palabras, se comporta como una constante.

Según su pregunta, si todos los miembros de la clase son privados y definitivos, pero la clase no es definitiva, se puede heredar la misma clase, pero el miembro de la súper clase es inmutable, ya que la palabra clave final está asociada a ellos.

¿Podría aclarar por qué se requiere una palabra clave final antes de la clase cuando la estamos convirtiendo en una inmutable? Quiero decir, si declaramos todos sus atributos como privados y finales, entonces también es una clase inmutable, ¿no es así?

Lo siento si la pregunta parece fácil, pero estoy realmente confundido al respecto. Ayúdame.

Editado: Sé que una clase declarada como final no puede clasificarse como subcategoría ... Pero si cada atributo es privado y definitivo, ¿qué diferencia hace eso?


No necesitas estrictamente final para hacer una clase inmutable. Es decir, puedes hacer una clase inmutable sin que sea definitiva.

Sin embargo, si no lo hace definitivo, entonces es posible que alguien extienda una clase y cree una subclase que sea mutable (ya sea agregando nuevos campos mutables o anulando métodos de una manera que le permita mutar campos protegidos de la clase inmutable original). Este es un problema potencial: viola el Principio de Sustitución de Liskov , en el sentido de que usted esperaría que todos los subtipos conserven la propiedad de la inmutabilidad.

Por lo tanto, generalmente es una buena práctica hacer que las clases inmutables sean definitivas para evitar este riesgo.


Si se sella una clase inmutable de Foo ("final"), cualquier persona que reciba una referencia a un Foo puede estar seguro de que si Foo se implementó correctamente, la instancia a la que se hace referencia será de hecho inmutable. Si una clase inmutable no está sellada, entonces alguien que recibe una referencia a un Foo puede estar seguro de que si la clase real del objeto al que se hace referencia (que puede ser Foo o algún tipo de derivado implementado por alguna persona desconocida arbitraria ) se implementó correctamente, La instancia será inmutable. Dejar a Foo sin sellar significa que cualquier persona que confíe en Foo para ser inmutable tendrá que confiar en que todos los que escriban una clase que se deriva de Foo lo implementarán correctamente. Si uno quiere estar seguro de que cada referencia a un Foo , de hecho, tendrá como objetivo una instancia inmutable sin tener que depender de los autores de las clases derivadas para cumplir con el contrato, lo que hace que Foo final pueda ayudar en esa seguridad.

Por otro lado, la posibilidad de que una clase se derive de Foo pero viole su inmutabilidad no es terriblemente diferente de la posibilidad de que una clase que se deriva de cualquier otra clase pueda violar los contratos de su clase matriz. Cualquier código que acepte una referencia de cualquier tipo que pueda ser subclasificado por un código externo podría recibir una instancia de una subclase que viole el contrato de su padre.

La pregunta fundamental a la hora de decidir si se debe sellar una clase inmutable es la misma que para cualquier otra clase: si los beneficios de dejar el tipo sin sellar superan los peligros que se plantearían al hacerlo. En algunos casos, puede tener sentido tener una clase inmutable extensible, o incluso una clase o interfaz abstracta cuyas implementaciones concretas estén obligadas contractualmente a ser inmutables; por ejemplo, un paquete de dibujo puede tener una clase ImmutableShape con algunos campos concretos, propiedades y métodos para definir transformaciones 2D, pero un método de Draw abstracto, que permite la definición de los tipos de derivados ImmutablePolygon , ImmutableTextObject , ImmutableBezierCurve , etc. Si alguien implementa un ImmutableGradientFilledEllipse clase ImmutableGradientFilledEllipse , pero no logra que ese tipo haga su propia copia de un GradientColorSelector mutable, los colores de los polígonos rellenos de gradiente podrían cambiar de forma inesperada, pero eso sería culpa de la clase ImmutableGradientFilledEllipse y no del código de consumo. A pesar de la posibilidad de que una implementación rota no respete el contrato de "inmutabilidad", una clase de ImmutableShape extensible sería mucho más versátil que una sellada.


Si todos los métodos públicos y protegidos son definitivos y ninguno de ellos permite modificar campos privados, y todos los campos públicos y protegidos son definitivos e inmutables, entonces supongo que podría decirse que la clase es semi-inmutable, o una especie de constante.

Pero las cosas se descomponen cuando creas una subclase y necesitas anular los iguales y el código hash. Y no puedo porque los hiciste finales ... Así que todo está roto, así que haz la final de toda la clase para evitar que el programador sea un tonto por accidente.

Como alternativa a este tipo de inmutabilidad de versión bastarda, tiene varias opciones.

Si desea adjuntar datos adicionales a una instancia inmutable, use Map . Como si quisiera agregar edad al nombre, no haría la class NameAge extends String ... :-)

Si desea agregar métodos, cree una clase de funciones de utilidad estáticas. Eso es un poco torpe, pero es la forma actual de Java, por ejemplo, Apache commons está lleno de tales clases.

Si desea agregar métodos y datos adicionales, cree una clase contenedora con métodos delegados a métodos de la clase inmutable. Cualquier persona que necesite usar los métodos adicionales debe conocerlos de todos modos, y no hay mucha diferencia práctica en new MyWrapper(myImmutableObj) clase derivada no inmutable o hacer algo como el new MyWrapper(myImmutableObj) para muchos casos de uso.

Cuando realmente tiene que hacer referencia a un objeto imutable original (como almacenarlo en una clase existente, no puede cambiarlo), pero necesita los datos adicionales en algún lugar, debe utilizar el enfoque del Map para mantener los datos adicionales alrededor, o algo así.


Un objeto inmutable es un objeto cuyo estado se garantiza que permanecerá idéntico durante toda su vida útil. Si bien es perfectamente posible implementar la inmutabilidad sin final, su uso hace ese propósito explícito, para el ser humano (el desarrollador de software) y la máquina (el compilador).

Los objetos inmutables tienen algunas características muy deseables:

they are simple to understand and easy to use they are inherently thread-safe: they require no synchronization they make great building blocks for other objects

Claramente final nos va a ayudar a definir objetos inmutables. Primero en etiquetar nuestro objeto como inmutable, lo que lo hace fácil de usar y entender por otros programadores. Segundo, garantizar que el estado del objeto nunca cambie, lo que habilita la propiedad segura de subprocesos: los problemas de concurrencia de subprocesos son relevantes cuando un subproceso puede cambiar datos mientras que otro subproceso está leyendo los mismos datos. Debido a que un objeto inmutable nunca cambia sus datos, no es necesario sincronizar el acceso a él.

Crea una clase inmutable cumpliendo con todas las siguientes condiciones:

Declare all fields private final. Set all fields in the constructor. Don''t provide any methods that modify the state of the object; provide only getter methods (no setters). Declare the class final, so that no methods may be overridden. Ensure exclusive access to any mutable components, e.g. by returning copies.

Una clase declarada final no puede ser subclasificada. Otras clases no pueden extender la clase final. Proporciona algún beneficio a la seguridad y la seguridad de los hilos.



Como dice el apilador, final se asegura de que la clase no esté subclasificada. Eso es importante para que cualquier código que confíe en su inmutabilidad pueda hacerlo de manera segura.

Por ejemplo, los tipos inmutables (donde cada campo es también de un tipo inmutable) se pueden usar libremente entre hilos sin preocuparse por las carreras de datos, etc. Ahora considere:

public class Person { private final String name; public Person(String name) { this.name = name; } public String getName() { return name; } }

Parece que puedes compartir instancias de Person libremente a través de hilos sin ningún problema. Pero, ¿qué pasa cuando el objeto que estás compartiendo es en realidad una subclase mutable?

public class Employee extends Person { private String company; public Employee(String name, String company) { super(name); this.company = company; } public void setCompany(String company) { this.company = company; } public String getCompany() { return company; } }

Ahora las instancias de Employee no son seguras de compartir entre subprocesos, porque no son inmutables. Pero el código que hace el intercambio solo puede conocerlos como instancias de Person ... lo que los lleva a una falsa sensación de seguridad.

Lo mismo ocurre con el almacenamiento en caché: debería ser seguro almacenar en caché y reutilizar tipos inmutables, ¿verdad? Bueno, es seguro almacenar en caché las instancias que son genuinamente de un tipo inmutable, pero si se trata de un tipo que no permite la mutación, pero permite las subclases, de repente ya no es seguro.

Piensa en java.lang.Object . No tiene campos mutables, pero es claramente una mala idea tratar cada referencia de Object como si fuera una referencia a un tipo inmutable. Básicamente depende de si piensas en la inmutabilidad como una propiedad del tipo o de los objetos. Un tipo verdaderamente inmutable declara "cada vez que vea una referencia de este tipo, puede tratarlo como inmutable", mientras que un tipo que permite una subclase arbitraria no puede hacer esa afirmación.

Además, hay una casa a mitad de camino: si puede limitar las subclases solo a lugares "confiables", puede asegurarse de que todo sea inmutable, pero aún así permitir esas subclases. El acceso en Java lo hace complicado, pero en C #, por ejemplo, podría tener una clase pública que solo permitiera crear subclases dentro del mismo ensamblaje, lo que brinda una API pública que es agradable y sólida en términos de inmutabilidad, al tiempo que permite los beneficios del polimorfismo. .