DEV ℧ Developer Diary

[EffectiveJava] item04 - 인스턴스화를 막거든 private 생성자를 사용하라.

인스턴스화를 막거든 private 생성자를 사용하라

코딩을 하다보면 static메서드와 static 필드만을 담은 클래스를 만들 경우가 있다. 대표적인 예를 든다면, util클래스가 그럴것이다.

자바에서 기본적으로 제공하는 클래스 중에서 본다면 java.lang.Math 클래스와 java.util.Arrays 클래스가 있다. 또한, java.util.Collections 클래스처럼 특정 인터페이스를 구현하는 객체를 생성해주는 정적메서드(혹은 팩토리)를 모아놓을 수도있다.

static 멤버만 담은 유틸리티 클래스는 인스턴스화하여 사용하는 경우가 없다. Math.ceil()Arrays.sort()처럼 static 메서드를 호출해서 사용한다.

만약 클래스에서 생성자를 명시하지 않을시 컴파일러가 자동으로 기본생성자를 만들고, 사용자는 해당 클래스의 생성자가 자동 생성 된것인지 구분 할수 없다. 이는 사용자가 해당 클래스를 인스턴스화 하여 사용할 수도 있게된다.

또한, 추상 클래스로 만드는 것으로는 인스턴스화를 막을 수 없다. 간단한 예시를 위해 아래의 예제코드를 살펴보자.

public abstract class UtilClass {

    public static String getHelloWorld() {
        return "HelloWorld";
    }

    static class ChildClass extends UtilClass {

    }
}

위의 자바코드를 아래처럼 인스턴스화 할 수 있다.

ChildClass childClass = new ChildClass();

추상화는 자식 클래스에서 상속받은 UtilClass의 getHelloWorld메서드는 사용 할 수 없지만, 의미없는 인스턴스가 생성되기 때문에 메모리 낭비를 가져올 수 있다. 또한 이를 본 사용자가 상속해서 쓰하는 뜻으로 오해할 수 있으니 더 큰문제가 될 수 있다.

그렇다면 해결방법이 없느냐? 아주 간단한 방법으로 해결 할 수 있다.

컴파일러가 기본생성자를 만드는 경우는 오직 명시된 생성자가 없을 때 뿐이니 private 생성자를 추가하면 클래스의 인스턴스화를 막을 수 있다.

위에서 예시로든 java.lang.Math, java.util.Arrays, java.util.Collections 또한 private로 생성자를 명시해서 인스턴스화를 막아주고 있다.

/* java.lang.Math */
public final class Math {
    /**
     * Don't let anyone instantiate this class.
     */
    private Math() {}
}

/* java.util.Arrays */
public class Arrays {

    // Suppresses default constructor, ensuring non-instantiability.
    private Arrays() {}
}

/* java.util.Collections */
public class Collections {
    // Suppresses default constructor, ensuring non-instantiability.
    private Collections() {}
}

만약 private 생성자 처리 및 클래스안에서 호출하는등의 만약의 일을 대비해 예외처리를 한다면

public class UtilClass {
    private UtilClass() {
        throw new AssertionError();
    }
}

AssertionError를 통해 예외처리를 할 수 있다.