(유효한 자바) 항목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());
}
}
-> 변경 불가능한 객체를 유지하기 위해 방어적 복사를 사용합니다.