[Effective Java] Item3. private 생성자나 열거 타입으로 싱글턴임을 보증하라

최강일·2024년 3월 23일
0

Effective Java

목록 보기
3/9

열거 타입

한정된 값만 갖는 데이터 타입

public enum Week{
	MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

Week today = Week.SUNDAY;

열거된 상수는 각 하나의 Week 객체다.

싱글턴

인스턴스를 오직 하나만 생성할 수 있는 클래스

이제 싱글턴을 만드는 방식을 알아본다.

방법1. public static final 필드 방식

pulbic class Elvis{
	public static final Elvis INSTANCE = new Elvis();
   
	//private 생성자
    private Elvis(){...}
    ...
}
  • 생성자를 private으로 감춘다.
  • public static final 필드를 이용해, 객체를 딱 한번만 생성한다.

장점

  • 코드가 간결하고 해당 클래스가 싱글턴임이 api에 명확히 드러난다.

취약점

  • 다만 클라이언트가 권한이 충분할 경우, 리플렉션 api인 AccessibleObject.setAccessible 을 사용하면 private 생성자에 접근이 가능하다.

방법2. static 팩터리 메서드 방식

public class Elvis{
	private static final Elvis INSTANCE = new Elvis();
    private Elvis(){...}

	public static Elvis getInstance(){
    	return Instance;
    }
}
  • 항상 같은 객체의 참조를 반환하므로 싱글턴 보장
  • 하지만 리플렉션 이슈 역시 존재함

장점

  • 팩터리 메서드를 사용하면 싱글턴이 아니게 언제든지 변경이 가능하다.
  • 원한다면 정적 팩토리를 "제네릭 싱글턴 팩토리"로 만들 수 있다.
  • 정적 팩토리의 메서드 참조를 공급자 (Supplier)로 사용할 수 있다.
public class Elvis{
	private static final Elvis INSTANCE = null;
    private Elvis(){...}

	public static Elvis getInstance(){
    	if(INSTANCE == null)
        	INSTANE = new Elvis();
    	return Instance;
    }
}

이 방법은 언제든지 static 팩터리 메서드를 수정하여 싱글턴이 아니게 만들 수 있다.

위 두 가지 방식으로 만든 싱글턴 객체를 직렬화 하려면 Serializable을 implement하는 것으론 모자라다.
모든 인스턴스 필드를 transient(직렬화 할 때 제외)로 선언하고 readResolve 메서드를 제공해야 한다.
이렇게 하지 않으면 역직렬화를 할 때 마다 새로운 인스턴스가 생겨나기 때문이다.

private Object readResolve() {
	// '진짜' Elvis를 반환하고 오버라이드 한 역직렬화 과정에서 생겨난 Elvis는 Garbage Collector에 맡긴다
    return INSTANCE;
}

방법3. 열거 타입 방식

public enum Elvis {
		INSTANCE;
		
		public void leaveTheBuliding() {
			...
		}
}

이해 +

Elvis elvis = Elvis.INSTANCE;

열거 변수를 만들어 새로운 객체를 생성하는 것처럼 보여도, heap 영역에 있는 열거 객체의 주소값을 바라보게 하는 원리다.

열거 타입 클래스를 만들고 상수를 지정하지만, 그 상수의 내용을 담고 있는 객체의 실체는 저 멀리 heap 영역에 있다.
열거 상수에는 열거 객체를 가르키는 주소가 담겨져 있는 원리이고, 열거 변수에는 열거 상수에 담겨져 있는 열거 객체의 주소값이 들어간다.

설명

1번 방식과 비슷하지만 더 단순하고 더 간결하고 추가 노력없이 직렬화할 수 있는 방법이다.
어색해 보일 수 있으나 대부분 상황에서 원소가 하나 뿐인 enum이 싱글턴을 만드는 가장 좋은 방법이다.

단, 만들려는 싱글턴이 enum외의 클래스를 상속해야 한다면 이 방법은 사용할 수 없다.
()

profile
Search & Backend Engineer

0개의 댓글