tonykang22 / study

0 stars 0 forks source link

[Effective Java] 아이템 3. 생성자나 열거 타입으로 싱글턴임을 보증하라. #36

Open tonykang22 opened 2 years ago

tonykang22 commented 2 years ago

아이템 3. 생성자나 열거 타입으로 싱글턴임을 보증하라.


첫번째 방법: private 생성자 + public static final 필드


첫번째 방법 예시 코드

public class Elvis implements IElvis, Serializable {

    public static final Elvis INSTANCE = new Elvis();
    private static boolean created;

    // 리플렉션에 의해 싱글톤이 깨지는 것을 막기 위한 방법
    private Elvis() {
        if (created) {
            throw new UnsupportedOperationException("can't be created by constructor.");
        }
        created = true;
    }

    public void sing() {
        System.out.println("I'll have a blue~ Christmas without you~");
    }

    private Object readResolve() {
        return INSTANCE;
    }
}
public class ElvisReflection {

    public static void main(String[] args) {
        try {
            Constructor<Elvis> defaultConstructor = Elvis.class.getDeclaredConstructor();
            defaultConstructor.setAccessible(true);
            // 리플렉션에 의해 싱글톤이 깨지는 것을 막기 위한 방법을 사용하지 않으면 새로운 인스턴스가 2개 생성된다.
            Elvis elvis1 = defaultConstructor.newInstance();
            Elvis elvis2 = defaultConstructor.newInstance();
            Elvis.INSTANCE.sing();
        } catch (InvocationTargetException | NoSuchMethodException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

}


두번째 방법: private 생성자 + 정적 팩터리 메서드


두번째 방법 예시 코드


장점 1. 예시 코드

public class Elvis implements Singer {
    private static final Elvis INSTANCE = new Elvis();
    private Elvis() { }
    // public static Elvis getInstance() { return new Elvis(); }
    public static Elvis getInstance() { return INSTANCE; }

    public void leaveTheBuilding() {
        System.out.println("Whoa baby, I'm outta here!");
    }


장점 2. 예시 코드

public class MetaElvis<T> {

    private static final MetaElvis<Object> INSTANCE = new MetaElvis<>();

    private MetaElvis() { }

    // 왜 <T>가 또 선언 되었는가? : 둘의 scope이 다르기 때문이다. MetaElvis<T>에는 클래스에서 정의한 <T>를 사용하는게 아니다.
    // public static <T> MetaElvis<T> getInstance() { return (MetaElvis<T>) INSTANCE; }
    // 그 예로 <E>로 바꾸어도 아무런 문제가 되지 않는다.
    @SuppressWarnings("unchecked")
    public static <E> MetaElvis<E> getInstance() { return (MetaElvis<E>) INSTANCE; }

    public void say(T t) {
        System.out.println(t);
    }

    public static void main(String[] args) {
        // 둘은 동일한 인스턴스이지만 각자 다른 타입으로 사용할 수 있는 경우.
        MetaElvis<String> elvis1 = MetaElvis.getInstance();
        MetaElvis<Integer> elvis2 = MetaElvis.getInstance();
        System.out.println(elvis1);
        System.out.println(elvis2);
        elvis1.say("hello");
        elvis2.say(100);
    }
}


장점 3. 예시 코드

public class Concert {

    public void start(Supplier<Singer> singerSupplier) {
        Singer singer = singerSupplier.get();
        singer.sing();
    }

    public static void main(String[] args) {
        Concert concert = new Concert();
        concert.start(Elvis::getInstance);
        // concert.start(() -> Elvis.getInstance());
    }
}


세번째 방법: 열거 타입

세번째 방법 예시 코드

public enum Elvis {
    INSTANCE;

    public void leaveTheBuilding() {
        System.out.println("기다려 자기야, 지금 나갈께!");
    }
}



완전 공략


메소드 참조

메소드 하나만 호출하는 람다 표현식을 줄여쓰는 방법


함수형 인터페이스

자바가 제공하는 기본 함수형 인터페이스


객체 직렬화

객체를 바이트스트림으로 상호 변환하는 기술