2008-09-28 39 views
19

Dice en this article que:clase inmutable debe ser definitiva?

Hacer una clase final porque es inmutable es una buena razón para hacerlo.

Estoy un poco sorprendido por esto ... entiendo que la inmutabilidad es una buena cosa desde el POV de hilos de seguridad y simplicidad, pero parece que estas preocupaciones son algo ortogonal a la extensibilidad. Entonces, ¿por qué la inmutabilidad es una buena razón para hacer una clase final?

Respuesta

9

Porque si la clase es definitiva no puede extenderla y hacerla mutable.

Incluso si hace que los campos sean definitivos, eso solo significa que no puede reasignar la referencia, no significa que no puede cambiar el objeto al que se hace referencia.

No veo mucho uso en un diseño para una clase inmutable que también debe extenderse, por lo que final ayuda a mantener la inmutabilidad intacta.

+3

Pero es posible que desee extender una clase inmutable para agregar propiedades adicionales de Immutbale. –

+2

"Porque si la clase es definitiva no puede extenderla y hacerla mutable" Pero no puede extender una clase inmutable no final y hacerla mutable, solo puede hacer que la subclase mutable –

+4

Como llamante, no sabe si está utilizando la clase inmutable o su subclase mutable, a menos que llame al constructor directamente. Eso es polimorfismo. – slim

10

Siguiendo el principio de sustitución de Liskov, una subclase puede extenderse pero nunca redefinir el contrato de su matriz. Si la clase base es inmutable, entonces es difícil encontrar ejemplos de dónde podría extenderse útilmente su funcionalidad sin romper el contrato.

Tenga en cuenta que, en principio, es posible extender una clase inmutable y cambiar los campos base, p. si la clase base contiene una referencia a una matriz, los elementos dentro de la matriz no se pueden declarar finales. Obviamente, la semántica de los métodos también se puede cambiar mediante la anulación.

Supongo que podría declarar todos los campos como privados y todos los métodos como definitivos, pero ¿cuál sería el uso de heredar?

+0

Digamos que tiene una clase Shape inmutable, que tiene una propiedad de área. Es posible que desee crear una subclase Circle que tenga propiedades adicionales, como el radio. Estas nuevas propiedades podrían ser inmutables, preservando así la propiedad de inmutabilidad del padre. ¿Qué está mal con eso? –

+1

No creo que haya nada de malo en eso, siempre y cuando seas la única persona que se extiende desde Shape. Pero tendrías que crear pautas precisas para otros desarrolladores y estar seguros de que los seguirían. Entonces creo que el argumento es que 99.El 9% de las veces no vale la pena el esfuerzo ... –

+0

GG está en el dinero. Si haces una garantía, debes conservarla. – CurtainDog

6

Principalmente creo que la seguridad. Por la misma razón, String es definitivo, cualquier cosa que un código relacionado con la seguridad quiera tratar como inmutable debe ser definitiva.

Supongamos que tiene una clase definida como inmutable, llámela MyUrlClass, pero no la marque como definitiva.

Ahora, alguien podría estar tentado a escribir código de administrador de seguridad como este;

void checkUrl(MyUrlClass testurl) throws SecurityException { 
    if (illegalDomains.contains(testurl.getDomain())) throw new SecurityException(); 
} 

Y esto es lo que les gustaría poner en su doRequest (MyUrlClass url) Método:

securitymanager.checkUrl(urltoconnect); 
Socket sckt = opensocket(urltoconnect); 
sendrequest(sckt); 
getresponse(sckt); 

Pero no pueden hacer esto, porque usted no hizo MyUrlClass final. La razón por la que no pueden hacerlo es que, si lo hicieran, el código podría evitar las restricciones del administrador de seguridad al anular getDomain() para que devuelva "www.google.com" la primera vez que se llame y "www.evilhackers.org". el segundo, y pasar un objeto de su clase a DoRequest().

tengo nada en contra de evilhackers.org, por cierto, si es que existe ...

En ausencia de problemas de seguridad es todo acerca de cómo evitar los errores de programación, y es, por supuesto, depende de cómo se Haz eso. Las subclases deben mantener el contrato de sus padres, y la inmutabilidad es solo una parte del contrato. Pero si se supone que las instancias de una clase son inmutables, entonces hacer que sea definitiva es una buena forma de asegurarse de que realmente sean inmutables (es decir, que no haya instancias mudables de subclases dando vueltas, que pueden usarse en cualquier lugar donde el padre clase es requerida).

No creo que el artículo al que hace referencia deba tomarse como una instrucción de que "todas las clases inmutables deben ser definitivas", especialmente si tiene un motivo positivo para diseñar su clase inmutable para la herencia. Lo que estaba diciendo es que proteger la inmutabilidad es una razón válida para el final, donde las preocupaciones de rendimiento imaginario (que es de lo que realmente se habla en ese momento) no son válidas. Tenga en cuenta que dio "una clase compleja no diseñada para la herencia" como una razón igualmente válida. Se puede argumentar de manera justa que no tener en cuenta la herencia en las clases complejas es algo que se debe evitar, así como no tener en cuenta la herencia en las clases inmutables. Pero si no puede explicarlo, al menos puede señalar este hecho previniéndolo.

+0

no sería suficiente con marcar los métodos 'final'? – Pacerier

+0

Como dije, "se puede argumentar con razón que no tener en cuenta la herencia en las clases complejas es algo que hay que evitar ... Pero si no puedes explicarlo, al menos puedes señalar este hecho al evitarlo". . Y lo estás argumentando. Bastante. –

0

Es una buena idea hacer una clase inmutable también por motivos de rendimiento. Tome Integer.valueOf por ejemplo. Cuando llamas a este método estático, no tiene que devolver una nueva instancia de Integer. Puede devolver una instancia previamente creada de forma segura sabiendo que cuando le pasó una referencia a esa instancia la última vez no la modificó (supongo que esto también es un buen razonamiento desde el punto de vista de la razón de seguridad).

Estoy de acuerdo con el punto de vista adoptado en Java efectivo en estos asuntos: que debe diseñar sus clases para la extensibilidad o hacerlas no extensibles. Si es su intención hacer algo extensible, quizás considere una interfaz o clase abstracta.

Además, no es necesario que la clase sea definitiva. Puedes hacer que los constructores sean privados.

+0

Solo unas pocas notas. 1) Por lo general, una instancia de clase mutable es mejor por razones de rendimiento en comparación con una instancia inmutable equivalente. Las instancias inmutables suelen ser seguras para subprocesos, pero cuestan inmutabilidad si desea obtener un objeto más nuevo incluso con un estado ligeramente diferente (utilizando un patrón Prototype o un Generador). 2) 'Integer.valueOf' es un ejemplo de un ejemplo de patrón de peso de vuelo, y el patrón generalmente no está relacionado con la cuestión de la inmutabilidad. 3) Tener un constructor privado no puede evitar la subclasificación usando una clase interna. –

10

La explicación de esto se da en el libro 'Effective Java'

Considere BigDecimal y BigInteger clases en Java.

no se entendía ampliamente que las clases inmutables tenían que ser eficazmente final de cuando BigInteger y BigDecimal fueron escritos, por lo que la totalidad de sus métodos pueden ser anulados. Desafortunadamente, esto no pudo ser corregido después del hecho mientras se preservaba la compatibilidad con versiones anteriores.

Si escribe una clase cuya seguridad depende de la inmutabilidad de un argumento BigInteger o BigDecimal de un cliente que no es de confianza, debe verificar que el argumento sea un BigEnteger o BigDecimal "real", en lugar de una instancia de una subclase no confiable. Si es el último, debes copiarlo defensivamente bajo la suposición de que podría ser mutable.

public static BigInteger safeInstance(BigInteger val) { 

    if (val.getClass() != BigInteger.class) 

    return new BigInteger(val.toByteArray()); 


     return val; 

    } 

Si permite que sub classing, podría romper la "pureza" del objeto inmutable.

Cuestiones relacionadas