Java에서 불변 클래스를 만드는 방법은 무엇입니까?

Java에서 불변성은 객체가 생성되면 내부 상태를 변경할 수 없음을 의미합니다. Java의 불변 클래스는 스레드 안전성, 손쉬운 디버깅 등과 같은 많은 이점을 제공합니다. 자바에서는 모든 래퍼 클래스 (Integer Boolean Byte Short와 같은) String 클래스는 변경할 수 없습니다. 우리는 불변 클래스를 직접 만들 수도 있습니다.

이 기사에서 우리는 다음을 배울 것입니다:

  • 불변성의 의미
  • 유용한 이유
  • 불변 클래스를 만드는 방법
  • 깊은 복사가 중요한 이유
  • Java 레코드 유형에는 어떤 제한이 있습니까?

불변 클래스란 무엇입니까?

불변 클래스는 일단 생성된 객체를 변경할 수 없는 클래스입니다. 수정을 하면 새로운 객체가 생성됩니다. 이 방법은 동시 애플리케이션에 사용됩니다.

불변 클래스 생성 규칙

  • 클래스는 다음과 같이 선언되어야 합니다. 결정적인 하위 클래스를 생성할 수 없도록 합니다.
  • 클래스의 데이터 멤버를 선언해야 합니다. 사적인 직접 접근이 불가능하도록 말이죠.
  • 클래스의 데이터 멤버는 다음과 같이 선언되어야 합니다. 결정적인 객체 생성 후에는 값을 변경할 수 없습니다.
  • 매개변수화된 생성자는 다음을 수행하는 모든 필드를 초기화해야 합니다. 딥 카피 개체 참조로 데이터 멤버를 수정할 수 없도록 합니다.
  • 실제 개체 참조를 반환하는 대신 복사본을 반환하려면 getter 메서드에서 개체의 전체 복사를 수행해야 합니다.

메모 : 설정자가 없어야 합니다. 간단히 말해서 인스턴스 변수의 값을 변경하는 옵션이 없어야 합니다.


예: 불변 클래스 구현

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

이 예에서는 이름이 지정된 최종 클래스를 만들었습니다. 학생. 여기에는 세 개의 최종 데이터 멤버(매개변수화된 생성자 및 getter 메소드)가 있습니다. 여기에는 setter 메소드가 없다는 점에 유의하십시오. 또한 래퍼 유형의 데이터 멤버는 이미 변경할 수 없으므로 깊은 복사나 복제를 수행할 필요가 없습니다.

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

원본 또는 반환된 Map을 수정한 후에도 Student 개체의 내부 상태는 변경되지 않습니다. 이는 불변성 개념을 확인시켜줍니다.

산출:

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


가변 필드가 있는 Java 레코드의 제한

자바 14 도입 기록 . 이는 클래스와 같은 불변성을 정의하는 명확하고 간결한 방법입니다.

기록 Student(문자열 이름 int regNo Map 메타데이터) {}


그러나 이는 얕은 불변성을 제공할 뿐입니다. 맵이 외부적으로 수정되면 레코드의 내부 상태가 변경됩니다.

지도 지도 = 새로운 HashMap <>();

map.put('1' '첫 번째');


Student s = new Student('ABC' 101 지도);


// 내부 상태 변경 - 안전하지 않음

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

s.metadata().put('3' '세 번째');

메모 : 모든 필드가 String int 또는 기타 레코드와 같이 변경할 수 없는 유형인 경우에만 레코드를 사용하십시오.