Objetos inmutables en Java

1. Información general

En este tutorial, aprenderemos qué hace que un objeto sea inmutable, cómo lograr la inmutabilidad en Java y qué ventajas conlleva hacerlo.

2. ¿Qué es un objeto inmutable?

Un objeto inmutable es un objeto cuyo estado interno permanece constante después de haber sido creado por completo .

Esto significa que la API pública de un objeto inmutable nos garantiza que se comportará de la misma forma durante toda su vida.

Si echamos un vistazo a la clase String , podemos ver que incluso cuando su API parece proporcionarnos un comportamiento mutable con su método de reemplazo , el String original no cambia:

String name = "baeldung"; String newName = name.replace("dung", "----"); assertEquals("baeldung", name); assertEquals("bael----", newName);

La API nos brinda métodos de solo lectura, nunca debe incluir métodos que cambien el estado interno del objeto.

3. La palabra clave final en Java

Antes de intentar lograr la inmutabilidad en Java, deberíamos hablar de la palabra clave final .

En Java, las variables son mutables de forma predeterminada, lo que significa que podemos cambiar el valor que tienen .

Al usar la palabra clave final al declarar una variable, el compilador de Java no nos permitirá cambiar el valor de esa variable. En su lugar, informará un error en tiempo de compilación:

final String name = "baeldung"; name = "bael...";

Tenga en cuenta que final solo nos prohíbe cambiar la referencia que contiene la variable, no nos protege de cambiar el estado interno del objeto al que se refiere mediante el uso de su API pública:

final List strings = new ArrayList(); assertEquals(0, strings.size()); strings.add("baeldung"); assertEquals(0, strings.size());

El segundo assertEquals fallará porque agregar un elemento a la lista cambia su tamaño, por lo tanto, no es un objeto inmutable.

4. Inmutabilidad en Java

Ahora que sabemos cómo evitar cambios en el contenido de una variable, podemos usarla para construir la API de objetos inmutables.

La construcción de la API de un objeto inmutable requiere que garanticemos que su estado interno no cambiará sin importar cómo usemos su API.

Un paso adelante en la dirección correcta es usar final al declarar sus atributos:

class Money { private final double amount; private final Currency currency; // ... }

Tenga en cuenta que Java nos garantiza que el valor de la cantidad no cambiará, ese es el caso de todas las variables de tipo primitivo.

Sin embargo, en nuestro ejemplo solo se nos garantiza que la moneda no cambiará, por lo que debemos confiar en la API de moneda para protegerse de los cambios .

La mayoría de las veces, necesitamos los atributos de un objeto para contener valores personalizados, y el lugar para inicializar el estado interno de un objeto inmutable es su constructor:

class Money { // ... public Money(double amount, Currency currency) { this.amount = amount; this.currency = currency; } public Currency getCurrency() { return currency; } public double getAmount() { return amount; } }

Como dijimos antes, para cumplir con los requisitos de una API inmutable, nuestra clase Money solo tiene métodos de solo lectura.

Usando la API de reflexión, podemos romper la inmutabilidad y cambiar objetos inmutables. Sin embargo, la reflexión viola la API pública del objeto inmutable y, por lo general, debemos evitar hacer esto.

5. Beneficios

Dado que el estado interno de un objeto inmutable permanece constante en el tiempo, podemos compartirlo de forma segura entre varios subprocesos .

También podemos usarlo libremente, y ninguno de los objetos que hacen referencia a él notará ninguna diferencia, podemos decir que los objetos inmutables están libres de efectos secundarios .

6. Conclusión

Los objetos inmutables no cambian su estado interno a tiempo, son seguros para subprocesos y libres de efectos secundarios. Debido a esas propiedades, los objetos inmutables también son especialmente útiles cuando se trata de entornos de subprocesos múltiples.

Puede encontrar los ejemplos utilizados en este artículo en GitHub.