String es una clase inmutable en Java. Una clase inmutable es simplemente una clase cuyas instancias no pueden modificarse. ¿Por qué el lenguaje de programación Java elige hacer que los objetos de clase String sean inmutables?
String es una clase inmutable en Java. Una clase inmutable es simplemente una clase cuyas instancias no pueden modificarse. ¿Por qué el lenguaje de programación Java elige hacer que los objetos de clase String sean inmutables?
Respuestas:
Este problema está fuertemente relacionado con la noción de lo que significa ser una instancia de una clase. En términos estrictos orientados a objetos, una clase tiene un invariante asociado: un predicado que siempre es válido al salir de un método (público) de la clase. Tal noción es fundamental para garantizar que la herencia esté bien definida, por ejemplo (es parte del Principio de sustitución de Liskov ).
Uno de los problemas más perniciosos con Java es que es difícil evitar que el código del cliente rompa invariantes de clase.
Por ejemplo, considere la siguiente clase 'ZipCode':
class ZipCode {
private String zipCode;
public ZipCode(String value){
if(!isValidZipCode(value))
throw new IllegalArgumentException();
zipCode = value;
assert(invariant());
}
public String get() { return zipCode; }
public boolean invariant() {
return isValidZipCode( zipCode );
}
}
Si String no fuera inmutable, un usuario de ZipCode podría llamar a 'get' y cambiar los caracteres en cualquier momento posterior, rompiendo así la invariante y destruyendo la integridad conceptual que ofrece la encapsulación del concepto ZipCode.
Dado que este tipo de integridad es esencial para garantizar que los sistemas grandes sean válidos, esta respuesta a su pregunta realmente exige la más amplia de:
"¿Por qué Java no admite un análogo de C ++ const, o al menos ofrece versiones inmutables de más de sus clases de biblioteca?"
Cosas como cadenas y fechas son valores naturales. En términos de C ++, esperamos que tengan un constructor de copia, un operador de asignación y un operador de igualdad, pero nunca esperamos tomar su dirección. Por lo tanto, no esperamos que se asignen individualmente en el montón. Los métodos virtuales no tienen sentido.
Los objetos de dominio son referencias naturales. Los C ++ no tienen constructor de copia, operador de asignación u operador de igualdad (son iguales solo si son idénticos). Podemos tomar su dirección y esperamos que se les asigne un montón. Los métodos son generalmente virtuales.
Java no tiene clases de valor, solo clases de referencia. Los valores están falsificados con objetos inmutables. Esto es cierto para las cadenas, pero desafortunadamente no para las fechas. La mutabilidad de las fechas de Java ha causado problemas frecuentes y ahora está en desuso. Los valores mutables no se pueden usar como base para un hash, por ejemplo.
Java fue diseñado para permitir la ejecución de subsecciones del código de un programa en entornos con restricciones de seguridad. La forma en que se implementó este requisito fue estableciendo un "SecurityManager" en un subproceso que tiene acceso a los parámetros de ciertas operaciones críticas (por ejemplo, abrir un archivo) y se le preguntó si la operación debería permitirse o no. Si las cadenas de Java fueran mutables, un programa podría eludir tales restricciones creando dos hilos, uno que realizara una operación de archivo abierto que se permitiría mientras que el otro modificaba la cadena en la que almacenaba el nombre de archivo en uno que no se permitiría. Existe entonces la posibilidad de que el administrador de seguridad lea la cadena original, acepte la operación, que se pasaría al código de apertura del archivo que luego precedería a abrir el segundo archivo (no permitido).
La última posibilidad haría que todas estas operaciones se ejecuten más lentamente y sería más probable que la implementación contenga errores, por lo que el uso de cadenas inmutables fue la decisión más sensata.
En términos más generales, los objetos inmutables son útiles porque permiten compartir sin necesidad de hacer copias defensivas (lo que puede ser necesario incluso en un código no crítico para la seguridad para evitar errores cuando cambian los datos de origen), por lo que incluso sin este requisito la decisión aún sería Una razonable.