Contrariamente a lo que mucha gente cree, hacer una clase inmutable final
es no es necesario.
El argumento estándar para hacer que las clases inmutables final
sea que si no lo hace, las subclases pueden agregar mutabilidad, violando así el contrato de la superclase. Los clientes de la clase asumirán la inmutabilidad, pero se sorprenderán cuando algo se mude debajo de ellos.
Si se toma este argumento a su extremo lógico, entonces todos métodos deben hacerse final
, ya que de lo contrario una subclase podría reemplazar un método de una manera que no sea conforme con el contrato de su superclase. Es interesante que la mayoría de los programadores de Java ven esto como algo ridículo, pero de alguna manera están de acuerdo con la idea de que las clases inmutables deberían ser final
. Sospecho que tiene algo que ver con los programadores de Java que, en general, no se sienten del todo cómodos con la noción de inmutabilidad, y tal vez con algún tipo de pensamiento difuso relacionado con los múltiples significados de la palabra clave final
en Java.
El compilador no puede o debe hacer cumplir el contrato de su superclase. El compilador puede hacer cumplir ciertos aspectos de su contrato (p. Ej .: un conjunto mínimo de métodos y sus firmas de tipo), pero hay muchas partes de contratos típicos que el compilador no puede aplicar.
La inmutabilidad forma parte del contrato de una clase. Es un poco diferente de algunas de las cosas a las que las personas están más acostumbradas, porque dice que la clase (y todas las subclases) no pueden hacer, mientras que la mayoría de los programadores Java (y generalmente OOP) tienden a pensar en contratos como relacionado con lo que una clase puede hacer, no lo que no puede hacer.
inmutabilidad también afecta a algo más que un solo método — que afecta a toda la instancia — pero esto no es realmente muy diferente a la forma en equals
y hashCode
en el trabajo de Java. Esos dos métodos tienen un contrato específico establecido en Object
. Este contrato establece muy cuidadosamente las cosas que estos métodos no pueden hacer. Este contrato se hace más específico en subclases. Es muy fácil anular equals
o hashCode
de una manera que infringe el contrato. De hecho, si anula solo uno de estos dos métodos sin el otro, es probable que esté violando el contrato. Entonces, ¿deberían equals
y hashCode
haberse declarado final
en Object
para evitar esto? Creo que la mayoría argumentaría que no deberían. Del mismo modo, no es necesario hacer clases inmutables final
.
Dicho esto, la mayoría de sus clases, inmutables o no, probablemente debería ser final
. Ver Effective Java Second Edition Elemento 17: "Diseñar y documentar para herencia o prohibirlo".
Así que una versión correcta de su paso 3 sería: "Haga que la clase sea definitiva o, cuando diseñe para la creación de subclases, documente claramente que todas las subclases deben seguir siendo inmutables".
La clase 'java.math.BigInteger' es un ejemplo, sus valores son inmutables pero no es definitiva. –
@Nandkumar Si tiene un 'BigInteger', no sabe si es inmutable o no. Es un diseño desordenado./'java.io.File' es un ejemplo más interesante. –
No permita que las subclases anulen los métodos: la forma más sencilla de hacerlo es declarar la clase como definitiva. Un enfoque más sofisticado es hacer que el constructor sea privado y construir instancias en métodos de fábrica: desde https://docs.oracle.com/javase/tutorial/essential/concurrency/imstrat.html –