해당 내용의 핵심은 인스턴스를 생성할 필요가 없는 클래스를 설계할 때, 개발자가 실수로 인스턴스를 생성하는 것을 방지합니다.
예를 들어, 유틸리티성 클래스를 만들 때 이 클래스는 정적 필드만 포함하고 인스턴스화할 필요가 없습니다. "Java Math " 클래스가 이러한 유형입니다. 자바에서는 생성자를 명시적으로 정의하지 않으면 컴파일러가 자동으로 기본 생성자를 제공합니다. 이로 인해 개발자가 실수로 이러한 유틸 클래스의 인스턴스를 생성할 수 있습니다.
이를 방지 하기 위해 private 생성자를 사용합니다. private 생성자는 외부에서의 접근을 차단하여 클래스의 인스턴스화를 방지 합니다. 또한 이 생성자 내부에는 인스턴스화를 시도할 경우 예외를 던지는 코드를 작성할 수도 있습니다.
public final class Math {
private Math() {}
public static final double PI = 3.14159265358979323846;
private static final double DEGREES_TO_RADIANS = 0.017453292519943295;
private static final double RADIANS_TO_DEGREES = 57.29577951308232;
@HotSpotIntrinsicCandidate
public static double sin(double a) {
return StrictMath.sin(a); // default impl. delegates to StrictMath
}
@HotSpotIntrinsicCandidate
public static double cos(double a) {
return StrictMath.cos(a); // default impl. delegates to StrictMath
}
}
public class UtilityClass {
// 기본 생성자가 자동 생성되지 않도록 private 생성자를 선언
private UtilityClass() {
throw new AssertionError("Utility class should not be instantiated");
}
public static void utilityMethod() {
// 유틸리티 메서드 구현
}
}
private 생성자를 사용함으로써 외부에서 "newUtlityClass" 를 호출하는 것이 불가능 합니다. 또한, 생성자 내부에서 "AssertionError" 를 던지는 것은 클래스 내부에서 실수로 생성자를 호출하는 것을 방지하기 위한 추가적인 조치입니다.
이런 방식으로 클래스 설계 시 private 생성자를 사용하면, 명시적으로 인스턴스화를 방지할 수 있으며, 이는 클래스의 설계 의도를 더 명확하게 전달하는데 도움이 됩니다.
스프링 프레임워크 기준에서 보면 유틸성 클래스( 인스턴스를 만들 필요가 없는 ) 의 경우 대부분 abstract 로 만들어 인스턴스화를 막았습니다. 하지만 여기에는 문제점이 존재하게 되는데요. abstract를 선언한다고 해도 익명 클래스를 구현하여 인스턴스를 만들 수 있으며, 상속을 받아 상위 클래스를 생성하는 문제점이 발생하게 됩니다.
public abstract class UtilClassExample {
// 기본 생성자는 별도로 만들지 않아도 default 로 생성이 됩니다.
public UtilClassExample() {
System.out.println("true = " + true);
}
public static LocalDateTime now() {
return LocalDateTime.now();
}
}
public class TimeClassExam extends UtilClassExample {
public static void main(String[] args) {
TimeClassExam utilClassExample = new TimeClassExam();
}
}
위와 같이 상속을 받게되면 자동적으로 인스턴스를 생성할 때, super 이 호출 되므로 생성자가 호출 되게 됩니다. 그러므로 private 로 인스턴스 생성을 명시적으로 제한해야 합니다.
참고 - 유틸클래스 기준
- 유틸 클래스는 인스턴스 변수가 없어야 합니다. 즉 모든 메서드는 정적(static)이여야 합니다.
- 클래스 내의 메서드들은 범용적이고 재사용이 가능하야 합니다. 특정 상황에 국한되지 않는 일반적인 기능이여야 합니다 ( 비즈니스 로직 x )
- 유틸클래스는 인스턴스화 되면 안됩니다.
'이펙티브 자바' 카테고리의 다른 글
불필요한 객체 생성을 피하라 (0) | 2024.01.11 |
---|---|
자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2024.01.10 |
private 생성자나 열거타입으로 싱글턴임을 보증하라 (0) | 2024.01.03 |
생성자에 매개변수가 많다면 빌더를 고려하라 (1) | 2023.12.21 |
정적 팩터리 메서드 (1) | 2023.12.20 |