[Effective Java] 아이템4 - 인스턴스화를 막으려거든 private 생성자를 사용하라

Note: 이 글은 Effective Java 책을 읽으면서 중요한 내용 및 알아야 하는 지식들을 정리한 글입니다.
또한, 백기선님의 Youtube 강의영상도 참고 했습니다.

단순히 정적 메서드와 정적 필드만을 담은 클래스를 만들고 싶을 때가 있다. class를 인스턴스화하지 못하게 하고, 기본 타입 값이나 변하지 않는 메서드들을 모아놓기 위해서이다.

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

    public static double sqrt(double a) {
        return StrictMath.sqrt(a);
    }
}

java.lang.Math 를 보면, 생성자를 private으로 만들고 주석으로 인스턴스화 하지 말라고 적어 놓았다. 이 클래스는 정적 메서드, 정적 필드만을 가지고 있으며 클라이언트는 해당 클래스를 Math.sqrt(100) 으로 밖에 쓰지 못한다.

추상 클래스로 만드는 것으로는 인스턴스화를 막을 수 없다.

하위 클래스를 만들어 인스턴스화 하면 그만이다.

public abstract class UtilClass {

  // 하위클래스를 만든다.
	static class AnotherClass extends UtilClass {

	}

	public static String getName() {
		return "SunMin";
	}

	public static void main(String[] args) {		
		AnotherClass subclass = new AnotherClass();
		System.out.print(subclass.getName());
	}

}

컴파일러는 생성자를 명시하지 않으면 자동으로 기본 생성자를 만들어주기 때문에, 명시적으로 생성자를 private으로 만들어서 아무도 인스턴화하지 못하게 만들면 된다.

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

이 방식은 상속을 불가능하게 하는 효과도 있다. 모든 생성자는 상위 클래스의 생성자를 호출하게 되는데, 이를 private으로 선언하면 하위클래스에서 상속을 못하기 때문이다.

스프링 Util 클래스가 인스턴스화를 막는 방법

책에서 나오듯이 인스턴스화를 막기 위해서 생성자를 private으로 선언할수도 있지만 현실적으로는 보통 abstract class로 만들어서 쓰는 것을 확인할 수 있다. 백기선님 유튜브 내용이다.

public abstract class StringUtils {}
public abstract class AnnotationUtils {}

위와 같이 org.springframework 에서 제공하는 Util은 abstract 클래스로 만들어진 것을 확인할 수 있다.

유튜브에서는 인스턴스화 하더라도 상위 메서드들을 호출 못한다고 했는데, 테스트 해보니 호출할수는 있는 것 같다.

대신 warning이 뜨면서, AnotherClass.getName 이나 UtilClass.getName 을 쓰라고 나온다.

Summary: Util 성향의 class를 만드려면 책에서 말한것처럼 private 생성자로 인스턴스화를 막거나, 스프링처럼 abstract 클래스를 만들고 하위 메서드들을 모두 static으로 하면 될 것 같다.

댓글남기기