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

들어가며

싱글턴이란 인스턴스를 오직 하나만 생성할 수 있는 클래스를 말한다.

public static final 필드 방식의 싱글턴

private 생성자는 public static final 필드인 Elvis.INSTANCE를 초기화할 때 딱 한번만 호출된다. public이나 protected 생성자가 없으므로 Elvis 클래스가 초기화될 때 만들어진 인스턴스가 전체 시스템에서 하나뿐임이 보장된다. 리플렉션을 이용해 private 생성자를 호출하게 하려할 경우 두번째 객체를 생성할 때 예외를 처리하면 된다.

1
2
3
4
5
6
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }

public void leaveTheBuilding() {...}
}

static 팩터리 메소드를 이용한 싱글턴

static 팩터리 메소드를 이용해 싱글턴을 만들면 첫번째 장점은 API를 바꾸지 않고도 싱글턴이 아니게 변경할 수 있다. 두번째 장점은 정적 팩터리를 제네릭 싱글턴 팩터리로 만들 수 있다. 세번째 장점은 정적 팩터리의 메서드 참조를 공급자(supplier)로 사용할 수 있다.

1
2
3
4
5
6
7
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public static Elvis getInstance() { return INSTANCE; }

public void leaveTheBuilding() { ... }
}

enum을 이용한 싱글턴

일반적인 싱글톤에서는 직렬화를 수행할 때 싱글턴이 싱글턴이 아닌 문제가 생긴다. 직렬화를 위해서는 readObject()를 구현해야 하는데 readObject()는 매번 새로운 인스턴스를 리턴하기 때문이다. 이를 위해선 다음과 같이 해결해야 한다.

  1. 싱글턴 오브젝트 직렬화를 위한 Serializable 상속
  2. 모든 필드를 transient로 선언
  3. readResolve() 메소드 구현

하지만 enum을 이용한 싱글턴에서는 위와 같은 과정이 필요없다. JVM이 Enum의 Serialization을 보장해준다. 또한 enum은 thread safe하게 구현되었기 때문에 멀티 스레드 환경에서 스레드와 관련된 작업을 처리하지 않아도 된다. 다만 Lazy Loding이라는 단점이 존재한다.

1
2
3
4
5
public enum Elvis {
INSTANCE;

public void leaveTheBuilding() { ... }
}
Author: Song Hayoung
Link: https://songhayoung.github.io/2020/08/04/Languages/Effective%20JAVA/item3/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.