Як створити незмінний клас у Java?

У Java незмінність означає, що після створення об’єкта його внутрішній стан не можна змінити. Незмінні класи в Java надають багато переваг, наприклад безпеку потоків, просте налагодження тощо. На Java все класи-обгортки (наприклад, Integer Boolean Byte Short), а клас String є незмінним. Ми також можемо створити свій власний незмінний клас.

У цій статті ми дізнаємося:

  • Що означає незмінність
  • Чому це корисно
  • Як створити наш власний незмінний клас
  • Чому глибоке копіювання важливо
  • Які обмеження мають типи записів Java

Що таке незмінний клас?

Незмінний клас — це клас, створені об’єкти якого неможливо змінити. Якщо ми робимо будь-які зміни, це призводить до нового об’єкта. Цей метод використовується в паралельних програмах.

Правила створення незмінного класу

  • Клас повинен бути оголошений як остаточний так що дочірні класи не можуть бути створені.
  • Члени даних у класі повинні бути оголошені приватний щоб прямий доступ був заборонений.
  • Члени даних у класі мають бути оголошені як остаточний щоб ми не могли змінити їх значення після створення об’єкта.
  • Параметризований конструктор повинен ініціалізувати всі поля, що виконують a глибока копія щоб члени даних не могли бути змінені за допомогою посилання на об’єкт.
  • Глибоке копіювання об’єктів має виконуватися в методах отримання, щоб повертати копію, а не повертати фактичне посилання на об’єкт.

Примітка : Не повинно бути сеттерів або, простіше кажучи, не повинно бути можливості змінити значення змінної екземпляра.


Приклад: реалізація незмінного класу

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

У цьому прикладі ми створили остаточний клас під назвою студент. Він має три кінцевих члени даних: параметризований конструктор і методи отримання. Зверніть увагу, що тут немає методу встановлення. Також зауважте, що нам не потрібно виконувати глибоке копіювання або клонування членів даних типів оболонки, оскільки вони вже незмінні.

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

Навіть після модифікації оригінальної або повернутої карти внутрішній стан об’єкта Student залишається незмінним. Це підтверджує концепцію незмінності.

Вихід:

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


Обмеження запису Java зі змінними полями

Представлено Java 14 запис . Це чіткий і стислий спосіб визначення незмінних подібних класів:

запис Student(String name int regNo Map метадані) {}


Але це лише пропонує неглибоку незмінність. Якщо карту змінено ззовні, внутрішній стан запису змінюється:

Карта map = нова HashMap <>();

map.put('1' 'перший');


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


// Змінює внутрішній стан — НЕбезпечно

map.put('2' 'другий');

s.metadata().put('3' 'третій');

Примітка : використовуйте запис, лише якщо всі поля є незмінними типами, наприклад String int або інші записи.