정적 메서드와 정적 필드만을 담은 클래스는 객체 지향적으로 사고하지 않는 이들이 종종 남용하는 방식이지만 분명 나름의 쓰임새가 있다.
예를 들어 java.lang.Math와 java.util.Arrays 처럼 기본 타입 값이나 배열 관련 메서드들을 모아놓을 수 있다.
또한, java.util.Collections처럼 특정 인터페이스를 구현하는 객체를 생성해주는 정적 메서드(또는 팩토리)를 모아놓을 수도 있다.
그런데 이런 정적 멤버만 담은 클래스는 인스턴스로 만들어 쓰려고 설계한게 아니다. 아래 예시를 보자.
static메서드만 가지고 있는 어떤 클래스가 있다고 가정해보자.
public class UtilityClass {
public UtilityClass(){}
public static Character getFirstIndexChar(String s){
return s.charAt(0);
}
...
}
public static void main(String[] args) {
UtilityClass utilityClass = new UtilityClass();
//UtilityClass.getFirstIndexChar("Hello");
utilityClass.getFirstIndexChar("Hello"); // 인스턴스 생성 후 호출
}
위 처럼 static 메서드를 인스턴스 생성 후 호출하는 것은 권장하는 방식은 아니지만, 문법적으로는 문제가 없다.
그런데 위 메서드가 인스턴스 메서드인지 스태틱 메서드인지 헷갈리게 만든다.
따라서 유틸리티 클래스 자체에서 인스턴스를 만드는것을 방지해야 한다.
인스턴스화를 막는법
방법1. abstract 키워드를 사용한다.
public abstract class UtilityClass {
public UtilityClass(){}
public static Character getFirstIndexChar(String s){
return s.charAt(0);
}
...
}
자바에선 abstract 키워드를 붙히면 인스턴스 생성이 불가능하다.
그러나, 추상 클래스로 만들어도 인스턴스가 만들어질 수 있다.
class IntegerUtilityClass extends UtilityClass{
// 기본 생성자
public IntegerUtilityClass() {
super();
}
}
추상 클래스를 상속받으면 기본 생성자가 만들어지고 생성자 내부에서 자동으로 super()를 호출하기 때문에, 부모 인스턴스가 생성된다.
따라서 이는 완벽하게 인스턴스화를 막을수 있는 방법이 아니며 정작 사용자는 이 클래스를 상속해서 쓰라는 뜻으로 오해할 수 있어서 문제가 발생할 수 있다.
방법2. 생성자를 private으로 제한
public class UtilityClass {
// Don't let anyone instantiate this class. (실제 java.lang에 명시된 주석)
private UtilityClass(){
throw new AssertionError();
}
public static Character getFirstIndexChar(String s){
return s.charAt(0);
}
}
생성자가 private이라면 그 어떤 외부에서도 접근할 수 없다.
클래스 안에서 실수로라도 생성자를 호출했을때, Error를 호출하게 한다면 완벽하게 막을 수 있다.
사용자는 "생성자가 있는데 왜 호출할 수 없게해뒀지?" 라고 생각할 수 있으니 위 처럼 주석을 달아놓으면 좋다.
또한 이 방식은 상속을 불가능하게 한다.
모든 생성자는 명시적이든 묵시적이든 상위 클래스의 생성자를 호출하게 되는데, 이를 private으로 선언하게 되면 하위 클래스가 상위 클래스의 생성자에 접근할 길이 없다.
💡참고로 Spring 대부분의 유틸리티 클래스는 방법2가 적용되어 있지 않고, 방법1의 abstract로 인스턴스화를 막고 있다.
아래는 Spring에서 지원하는 StringUtils 클래스이다.
실제로 abstract를 사용하고 있는 것을 확인할 수 있다.
'Java' 카테고리의 다른 글
Java) 생성자나 열거 타입으로 싱글턴임을 보증하라 (1) | 2024.11.05 |
---|---|
Java) 생성자에 매개변수가 많다면 빌더를 고려하라 (4) | 2024.11.04 |
Java) 생성자 대신 정적 팩토리 메서드 (1) | 2024.10.30 |