[vc_row][vc_column][vc_column_text]Las clases que representan valores deben tener las mismas propiedades que los tipos primitivos. Los tipos primitivos representan valores básicos como int o char en Java. Los tipos primitivos no tienen identidad y son inmutables.
A continuación, veremos que esas propiedades también son útiles para las clases que representan propiedades.
Sin Identidad
Dos valores son iguales si ellos tienen el mismo valor externo visible: Por ejemplo, si tiene dos variables (a y b) con el valor de 5, ellas son iguales:
1 2 3 | int a = 5; int b = 5; a == b // true |
[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]
Inmutable
Los valores son inmutables. Si modifica un valor, se convierte en un nuevo valor. En el siguiente ejemplo, modificamos la variable a, que conduce a un nuevo valor b. Las variables a y b no son iguales.
La clase de Valor A
Ahora veamos una clase que representa un valor con esas dos propiedades, la clase java.time.Instant. Esta clase representa un instante en el tiempo. Veamos primero la declaración de campo de esta clase:
1 2 3 4 5 6 7 | package java.time; public final class Instant { private final long seconds; private final int nanos; // static fields and methods omitted } |
Declarar los campos como final hacen que esta clase sea inmutable. Declarar un campo como final (como los dos campos en el ejemplo) permite al compilador verificar que los campos no se modifiquen después de que se haya llamado al constructor de la clase. Tenga en cuenta que el final es un modificador de campo. Hace que el campo sea inmutable, no el objeto al que hace referencia el campo. Entonces el tipo de campo final también debe ser inmutable o un tipo primitivo como en el ejemplo.
A continuación, veamos el método igual para ver cómo se implementa la igualdad para esta clase:
1 2 3 4 5 6 7 8 9 10 11 | public boolean equals(Object otherInstant) { if (this == otherInstant) { return true; } if (otherInstant instanceof Instant) { Instant other = (Instant) otherInstant; return this.seconds == other.seconds && this.nanos == other.nanos; } return false; } |
Tal como se observa, dos objetos Instant son iguales si sus estados externos visibles es igual[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]
Inmutabilidad utiliza la Identidad
Ahora veamos una clase que es inmutable pero usa su identidad para la igualdad: la clase Object. La clase Object es útil cuando solo necesita una identidad pero no un estado, como en el ejemplo siguiente, del JDK 9 java.uti.concurrent.CopyOnWriteArrayList, donde necesitamos la identidad como monitor para la sincronización:
1 2 3 4 5 6 7 8 9 | public class CopyOnWriteArrayList { final transient Object lock = new Object() public boolean add(E e) { synchronized (lock) { // other statements omitted } } } |
Representar el valor de una clase con una identidad no es útil.[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]
No Identity pero Mutable
La siguiente muestra una clase valor mutable, la clase java.util.Date:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public class Date { private transient BaseCalendar.Date cdate; private transient long fastTime; public void setTime(long time) { fastTime = time; cdate = null; } public boolean equals(Object obj) { return obj instanceof Date && getTime() == ((Date) obj).getTime(); } public long getTime() { return getTimeImpl(); } private final long getTimeImpl() { if (cdate != null && !cdate.isNormalized()) { normalize(); } return fastTime; } // static fields and other methods omitted } |
Los dos campos (cdate y fastTime) no son definitivos y se pueden cambiar con el método setTime, lo que hace que la clase sea mutable. El método igual verifica el estado visible externamente para la igualdad. Si bien es posible implementar valores con clases mutables, las clases inmutables son más fáciles de usar.[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]
Ventajas de una clase Inmutable
Las clases inmutables no pueden cambiar su estado. Esta propiedad es útil en escenarios específicos en programas de sub proceso único, por ejemplo, cuando los usa como claves en mapas hash. Y hace que escribir programas de sub procesos múltiples sea mucho más fácil.
Las clases inmutables no cambian su valor en el medio de una operación sin usar bloques sincronizados. Al evitar los bloques de sincronización, evita los bloqueos. Y como siempre trabajas con un estado constante e inmutable, evitas las condiciones de carrera.[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]
Uso de la Identidad
Si bien aún es posible acceder a la identidad de un objeto de valor, probablemente sea un error. Por ejemplo, el uso de == en lugar de equals es probablemente incorrecto:
1 2 3 4 | Integer a = new Integer(5); Integer b = new Integer(5); a.equals(b) // true a == b |
Probablemente esperas true cuando comparas dos Integers de valor 5, entonces el valor de == es incorrecto. Los siguientes métodos utilizan la identidad del objeto y se deben evitar al utilizar el valor de las clases:
- sentencias de sincronización
- System.identityHashCode
- Object.notify and wait
[/vc_column_text][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]
El futuro: JEP 169, Objetos como Valor
La implementación de valores utilizando clases requiere más memoria que sus contrapartes primitivas. Una solución a este problema es implementada por Java Enhancement Proposal 169, Value Objects. Le permitirá crear clases de valor con características de memoria similares a los tipos primitivos.
La idea es implementar un nuevo operador (lockPermanently), que convierte un objeto en un nuevo estado con un consumo de memoria similar a un tipo primitivo. El uso de una operación que requiera la identidad del objeto de valor como == o sincronizado en dicho objeto bloqueado estará prohibido.
Conclusion and What Is Next
Los tipos primitivos representan valores básicos. Los tipos primitivos son inmutables y no tienen identidad. Hemos visto cómo implementar clases con las mismas propiedades. El uso de la identidad de una clase de valor, aunque todavía es posible, es probablemente un error. Hacer que las clases de valor sean inmutables hace que sean más fáciles de usar, especialmente para el software seguro para subprocesos.[/vc_column_text][/vc_column][/vc_row]
Gracias por el regalo