Com crear una classe immutable a Java?

A Java, la immutabilitat significa que un cop creat un objecte no es pot canviar el seu estat intern. Les classes immutables a Java ofereixen molts avantatges com ara una depuració fàcil de seguretat de fils i tot. En Java tots els classes d'embolcall (com Integer Boolean Byte Short) i la classe String és immutable. També podem crear la nostra pròpia classe immutable.

En aquest article aprendrem:

  • Què vol dir immutabilitat
  • Per què és útil
  • Com crear la nostra pròpia classe immutable
  • Per què és important la còpia profunda
  • Quines són les limitacions que tenen els tipus de registre Java

Què és una classe immutable?

Una classe immutable és una classe els objectes de la qual no es poden canviar un cop creats. Si fem alguna modificació es tradueix en un objecte nou. Aquest mètode s'utilitza en aplicacions concurrents.

Regles per crear una classe immutable

  • La classe s'ha de declarar com final de manera que no es poden crear classes infantils.
  • S'han de declarar els membres de les dades de la classe privat de manera que no es permet l'accés directe.
  • Els membres de dades de la classe s'han de declarar com final de manera que no podem canviar-ne el valor després de la creació de l'objecte.
  • Un constructor parametritzat hauria d'inicialitzar tots els camps realitzant a còpia profunda de manera que els membres de les dades no es poden modificar amb una referència d'objecte.
  • La còpia profunda d'objectes s'ha de realitzar als mètodes d'obtenció per retornar una còpia en lloc de tornar la referència real de l'objecte.

Nota : No hi hauria d'haver configuradors o, en termes més senzills, no hi hauria d'haver cap opció per canviar el valor de la variable d'instància.


Exemple: implementació de classe immutable

Estudiant.java

Java
   // Java Program to Create An Immutable Class   import     java.util.HashMap  ;   import     java.util.Map  ;   // declare the class as final   final     class   Student     {      // make fields private and final      private     final     String     name  ;      private     final     int     regNo  ;      private     final     Map   <  String       String  >     metadata  ;      // initialize all fields via constructor      public     Student  (  String     name       int     regNo       Map   <  String       String  >     metadata  )     {      this  .  name     =     name  ;      this  .  regNo     =     regNo  ;      // deep copy of mutable object (Map)      Map   <  String       String  >     tempMap     =     new     HashMap   <>  ();      for     (  Map  .  Entry   <  String       String  >     entry     :     metadata  .  entrySet  ())     {      tempMap  .  put  (  entry  .  getKey  ()     entry  .  getValue  ());      }      this  .  metadata     =     tempMap  ;      }      // only provide getters (no setters)      public     String     getName  ()     {      return     name  ;      }      public     int     getRegNo  ()     {      return     regNo  ;      }      // return deep copy to avoid exposing internal state      public     Map   <  String       String  >     getMetadata  ()     {      Map   <  String       String  >     tempMap     =     new     HashMap   <>  ();      for     (  Map  .  Entry   <  String       String  >     entry     :     this  .  metadata  .  entrySet  ())     {      tempMap  .  put  (  entry  .  getKey  ()     entry  .  getValue  ());      }      return     tempMap  ;      }   }   

En aquest exemple hem creat una classe final anomenada Estudiant. Té tres membres de dades finals, un constructor parametritzat i mètodes d'obtenció. Tingueu en compte que aquí no hi ha cap mètode de configuració. Tingueu en compte també que no cal que realitzem una còpia profunda o la clonació dels membres de dades dels tipus d'embolcall, ja que ja són immutables.

Geeks.java:

Java
   import     java.util.HashMap  ;   import     java.util.Map  ;   public     class   Geeks     {      public     static     void     main  (  String  []     args  )     {      // create a map and adding data      Map   <  String       String  >     map     =     new     HashMap   <>  ();      map  .  put  (  '1'       'first'  );      map  .  put  (  '2'       'second'  );      // create an immutable Student object      Student     s     =     new     Student  (  'GFG'       101       map  );      // accessing data      System  .  out  .  println  (  s  .  getName  ());         System  .  out  .  println  (  s  .  getRegNo  ());         System  .  out  .  println  (  s  .  getMetadata  ());         // try to modify the original map      map  .  put  (  '3'       'third'  );      System  .  out  .  println  (  s  .  getMetadata  ());         // try to modify the map returned by getMetadata()      s  .  getMetadata  ().  put  (  '4'       'fourth'  );      System  .  out  .  println  (  s  .  getMetadata  ());         }   }   

Fins i tot després de modificar el mapa original o retornat, l'estat intern de l'objecte Student es manté sense canvis. Això confirma el concepte d'immutabilitat.

Sortida:

 GFG   
101
{1=first 2=second}
{1=first 2=second}
{1=first 2=second}


Limitació del registre Java amb camps mutables

S'ha introduït Java 14 registre . Aquesta és una manera clara i concisa de definir classes com immutables:

record Student(String name int regNo Map metadades) {}


Però això només ofereix una immutabilitat superficial. Si el mapa es modifica externament, l'estat intern del registre canvia:

Mapa mapa = nou HashMap <>();

map.put('1' 'primer');


Student s = new Student('ABC' 101 map);


// Canvia l'estat intern — NO és segur

map.put('2' 'segon');

s.metadata().put('3' 'tercer');

Nota : Utilitzeu el registre només si tots els camps són de tipus immutable com String int o altres registres.