Wie erstelle ich eine unveränderliche Klasse in Java?

In Java bedeutet Unveränderlichkeit, dass der interne Zustand eines Objekts nach der Erstellung nicht mehr geändert werden kann. Unveränderliche Klassen in Java bieten viele Vorteile wie Thread-Sicherheit, einfaches Debuggen und vieles mehr. In Java alle Wrapper-Klassen (wie Integer Boolean Byte Short) und die String-Klasse ist unveränderlich. Wir können auch unsere eigene unveränderliche Klasse erstellen.

In diesem Artikel erfahren wir:

  • Was Unveränderlichkeit bedeutet
  • Warum es nützlich ist
  • So erstellen Sie unsere eigene unveränderliche Klasse
  • Warum Deep Copying wichtig ist
  • Welche Einschränkungen gibt es bei Java-Datensatztypen?

Was ist eine unveränderliche Klasse?

Eine unveränderliche Klasse ist eine Klasse, deren Objekte nach ihrer Erstellung nicht mehr geändert werden können. Wenn wir Änderungen vornehmen, entsteht ein neues Objekt. Diese Methode wird in gleichzeitigen Anwendungen verwendet.

Regeln zum Erstellen einer unveränderlichen Klasse

  • Die Klasse muss als deklariert werden Finale sodass keine untergeordneten Klassen erstellt werden können.
  • Datenelemente in der Klasse müssen deklariert werden Privat sodass ein direkter Zugriff nicht möglich ist.
  • Datenelemente in der Klasse müssen als deklariert werden Finale sodass wir ihren Wert nach der Objekterstellung nicht ändern können.
  • Ein parametrisierter Konstruktor sollte alle Felder initialisieren, die a ausführen tiefe Kopie sodass Datenelemente nicht mit einer Objektreferenz geändert werden können.
  • Deep Copy von Objekten sollte in den Getter-Methoden durchgeführt werden, um eine Kopie zurückzugeben, anstatt die eigentliche Objektreferenz zurückzugeben.

Notiz : Es sollte keine Setter geben oder einfacher ausgedrückt, es sollte keine Option zum Ändern des Werts der Instanzvariablen geben.


Beispiel: Unveränderliche Klassenimplementierung

Student.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  ;      }   }   

In diesem Beispiel haben wir eine letzte Klasse mit dem Namen erstellt Student. Es verfügt über drei letzte Datenelemente, einen parametrisierten Konstruktor und Getter-Methoden. Bitte beachten Sie, dass es hier keine Setter-Methode gibt. Beachten Sie außerdem, dass wir kein Deep Copy oder Klonen von Datenelementen von Wrapper-Typen durchführen müssen, da diese bereits unveränderlich sind.

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  ());         }   }   

Auch nach der Änderung der ursprünglichen oder zurückgegebenen Map bleibt der interne Status des Student-Objekts unverändert. Dies bestätigt das Unveränderlichkeitskonzept.

Ausgabe:

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


Einschränkung des Java-Datensatzes mit veränderlichen Feldern

Java 14 eingeführt aufzeichnen . Dies ist eine klare und prägnante Möglichkeit, unveränderliche Klassen zu definieren:

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


Dies bietet jedoch nur oberflächliche Unveränderlichkeit. Wenn die Karte extern geändert wird, ändert sich der interne Status des Datensatzes:

Karte map = neue HashMap <>();

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


Student s = neuer Student('ABC' 101 Karte);


// Ändert den internen Status – NICHT sicher

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

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

Notiz : Datensatz nur verwenden, wenn alle Felder unveränderliche Typen wie String int oder andere Datensätze sind.