[이펙티브 자바] Item17. 변경

(유효한 자바) 항목17. 변경 가능성을 최소화합니다.

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

: 한마디로 내부 값을 변경할 수 없는 클래스말한다

불변 인스턴스에 저장된 정보는 고정되어 객체가 소멸될 때까지 변경되지 않습니다.

Java의 불변 클래스에는 String, BigInteger 및 BigDeciaml이 포함됩니다.

변경 불가능한 클래스는 변경 가능한 클래스보다 디자인, 구현 및 사용이 쉽고 오류가 덜 발생하며 훨씬 더 안전합니다.

불변 클래스를 만들기 위한 5가지 규칙

public final class PhoneNumber // 클래스를 확장할 수 없도록 한다.

{ private final short areaCode, prefix, lineNum; // 모든 필드를 final로 선언, 모든 필드는 private으로 선언 public PhoneNumber(short areaCode, short prefix, short lineNum) { this.areaCode = areaCode; this.prefix = prefix; this.lineNum = lineNum; } public short getAreaCode() { return areaCode; } public short getPrefix() { return prefix; } public short getLineNum() { return lineNum; } }

– 개체의 상태를 변경하는 메서드(수정자)를 제공하지 마십시오.

: setter와 같은 수정자는 제공되지 않습니다.

– 클래스를 확장 불가능하게 만듭니다.

: 클래스를 final로 선언하면 상속이 방지되고 하위 클래스가 객체의 상태를 우발적으로 또는 악의적으로 변경하는 것을 방지할 수 있습니다.

– 모든 필드는 최종으로 선언됩니다.

: 시스템이 강제하는 수단을 이용하여 설계자의 의도를 명확히 드러내는 방식입니다.

또한 새로 생성된 인스턴스가 동기화 없이 다른 스레드로 전달되더라도 제대로 작동하는지 확인해야 합니다.

– 모든 필드를 비공개로 선언합니다.

: 클라이언트가 필드에서 참조하는 변수 개체에 직접 액세스하고 수정하는 것을 방지합니다.

public class Address
{
   private String zipCode;
   private String street;
   private String city;
   
   public String getZipCode()
   {
      return zipCode;
   }
   
   public void setZipCode(String zipCode)
   {
      this.zipCode = zipCode;
   }
   
   public String getStreet()
   {
      return street;
   }
   
   public void setStreet(String street)
   {
      this.street = street;
   }
   
   public String getCity()
   {
      return city;
   }
   
   public void setCity(String city)
   {
      this.city = city;
   }
}
public final class Person
{
   private final Address address; // address가 가변 클래스라면... Person을 불변으로 만들어도 값이 바뀔수 있다.

public Person(Address address) { this.address = address; } // 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.

public Address getAddress() { return address; } public static void main(String() args) { Address seattle = new Address(); seattle.setCity("Seattle"); Person person = new Person(seattle); Address redmond = person.getAddress(); // 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.

redmond.setCity("Redmond"); System.out.println("person.address.getCity() = " + person.address.getCity()); } }

– 내부 변경 가능 구성 요소에 사용자 외에는 아무도 액세스할 수 없도록 합니다.

:Address는 가변 컴포넌트이므로 Person을 불변으로 만들더라도 getAddress로 수정할 수 있습니다.

따라서 클래스에 가변 객체를 참조하는 필드가 하나 이상 있으면 클라이언트가 개체에 대한 참조를 얻을 수 없도록 해야 합니다.

이러한 필드는 클라이언트 제공 개체 참조를 가리키면 안 되며 액세스 방법은 필드를 있는 그대로 반환하면 안 됩니다.

public final class Person
{
   private final Address address; // address가 가변 클래스라면... Person을 불변으로 만들어도 값이 바뀔수 있다.

public Person(Address address) { this.address = address; } // 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.

// public Address getAddress() // { // return address; // } // 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.

-> getter가 필요한 경우에는 방어적인 복사 public Address getAddress() { Address copyOfAddress = new Address(); copyOfAddress.setCity(address.getCity()); copyOfAddress.setStreet(address.getStreet()); copyOfAddress.setZipCode(address.getZipCode()); return copyOfAddress; } public static void main(String() args) { Address seattle = new Address(); seattle.setCity("Seattle"); Person person = new Person(seattle); Address redmond = person.getAddress(); // 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.

redmond.setCity("Redmond"); System.out.println("person.address.getCity() = " + person.address.getCity()); } }

-> 변경 불가능한 객체를 유지하기 위해 방어적 복사를 사용합니다.