요약
•
싱글톤 객체를 만들고 싶다면 readResolve 메서드를
직렬화와 싱글톤
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
...
}
Java
복사
•
위 코드는 클래스 바깥에서 생성자를 호출하지 못하도록 막는 방식으로 싱글턴 패턴을 구현한 것이다.
•
하지만 위 코드에 Serializable을 적용하면 더 이상 싱글턴 패턴이 아니게 된다.
•
기본 직렬화 형태를 사용하든 어떤 readObject를 사용하든, 역직렬화를 통해 생성된 인스턴스는 이 클래스가 초기화될 때 만들어진 인스턴스와는 별개의 인스턴스가 반환된다.
•
readResolve 기능으로 readObject가 만들어낸 인스턴스를 다른 것으로 대체하고, 새로 생성된 인스턴스는 가비지 컬렉터가 수거해가도록 할 수 있다.
private Object readResolve() {
return INSTANCE;
}
Java
복사
•
역직렬화 후 새로 생성된 객체를 인수로 readResolve 메서드가 호출되고, 이 메서드에서 반환된 객체 참조가 새로 생성된 객체를 대신해 반환된다.
•
이를 통해 Serializable을 구현하면서 싱글톤 패턴을 유지할 수 있다.
•
readResolve 메서드는 역직렬화한 객체를 무시하고 지정된 인스턴스를 반환하므로, 해당 클래스의 직렬화 형태는 아무런 데이터를 가질 이유가 없으니 모든 인스턴스 필드를 trasient로 선언해야 한다.
•
transient가 아닌 인스턴스 필드를 가지고 있으면 간단하게 역직렬화된 인스턴스를 훔쳐낼 수 있다.
•
final 클래스에서는 readResolve 메서드는 private로 선언되어있어야 한다.
•
이렇듯 readResolve 메서드를 사용해 만들어진 역직렬화된 인스턴스에 접근하지 못하게 하는 방법은 깨지기 쉽고 신경을 많이 써야 한다.
•
직렬화 가능한 인스턴스 클래스를 열거 타입으로 구현하면 선언한 상수 외의 다른 객체는 존재하지 않는게 보장되기 때문에, readResolve 메서드보다 열거 타입을 사용하자.
•
위 예시에서는 Elvis를 원소 하나짜리 열거 타입으로 바꾸는 것이 더 나은 선택이다.