첫번째 방법은 public static final 필드 방식의 싱글턴이다.
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() {}
}
Elvis elvis = Elvis.INSTANCE;
public이나 protected 생성자가 없기 때문에 전체 시스템에서 인스턴스가 하나만 생성됨을 보장할 수 있다.
하지만 리플렉션 API를 사용하면 인스턴스가 여러 개 생성될 수도 있다.
두 번째 방법은 정적 팩터리 메서드를 public static 멤버로 제공하는 방법이다.
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() {
}
public static Elvis getInstance() {
return INSTANCE;
}
}
Elvis elvis = Elvis.getInstance();
이 방법도 첫 번째 방법과 똑같이 리플렉션 API를 사용하면 인스턴스가 여러 개 생성될 수 있다.
이 방식의 장점은,
1. API를 바꾸지 않고도 싱글턴 이 아니게 변경할 수 있다.
class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() {
}
public static Elvis newInstance() {
// return INSTANCE;
return new Elvis();
}
}
Elvis elvis = Elvis.newInstance();
정적 팩토리를 제네릭 싱글턴 팩토리로 만들 수 있다.
제네릭 싱글턴 팩터리
제네릭으로 타입설정 가능한 인스턴스를 만들어두고, 반환 시에 제네릭으로 받은 타입을 이용해 타입을 결정하는 것이다.
https://jake-seo-dev.tistory.com/13
정적 팩토리 메서드 참조를 공급자로 사용할 수 있다. (Elvis::getInstance를 Supplier로 사용하는 식)
이러한 방식들이 굳이 필요하지 않다면 첫 번째 방법이 좋다.
다만 두 가지 방법은 싱글턴 클래스를 직렬화를 할 때 Serialzable을 구현하는 것만으로는 충분하지 않다.
모든 인스턴스 필드를 일시적(transient)이라고 선언하고 readResolve 메서드를 제공해야 한다. 이렇게 하지 않으면 역 직렬화를 할 때 새로운 인스턴스가 만들어진다.
세 번째 방법은 원소가 하나인 열거 타입을 선언하는 것이다.
직렬화를 위한 추가 구현이나 리플렉션 API를 통해서 인스턴스가 여러 개 생성되는 경우를 막지 않아도 된다. 또한 인스턴스가 하나만 생성되는 것이 보장된다.
대부분 상황에서는 원소가 하나뿐인 열거 타입이 싱글턴들 만드는 가장 좋은 방법이다.
다만, 열거 타입은 인터페이스 구현은 가능하지만 상속은 사용할 수 없다는 점이 있다.