💥 개요
정적 메서드와 정적 필드만을 담은 클래스는 객체지향적이라고 볼 수 없습니다.
하지만 분명 쓰임새가 있는데, 기본 타입 값(java.util.Arrays)이나 java.util.Collections처럼 특정 인터페이스를 구현하는 객체를 생성해주는 정적 메서드를 모아 놓을 수 있습니다.
자바 8부터는 이런 메서드를 인터페이스에 넣을 수 있게 되었습니다.
또한 final 클래스와 관련된 메서드들을 모아놓을 때도 사용합니다. final 클래스를 상속해 하위 클래스에 메서드를 넣는건 불가능하기 때문입니다.
🩻 문제
정적 멤버만 담은 유틸 클래스는 instance로 만들어 쓰려한게 아닙니다.
하지만 문제는 생성자를 명시하지 않으면 컴파일러는 기본 생성자를 자동으로 만듭니다.
그리고 추상클래스로 만드는 방법 또한 인스턴스화를 막을 수 없습니다.
하위 클래스를 만들어 인스턴스화 하면 되니까요.(상속해서 쓰라고 알아들으면 더욱 큰일!)
💡 해결방법
단순합니다. 기본 생성자를 private으로 만들면 해결됩니다.
public class UtilityClass {
// 기본 생성자가 만들어지는 것을 막는다(인스턴스화 방지용).
private UtilityClass() {
throw new AssertionError();
}
// 나머지 코드는 생략
}
이 방식은 생성자가 private이니 클래스 밖에서 사용이 불가하고, Error를 Throw하여 내부에서도 인스턴스화 되는것을 막아줍니다. 또한 상속을 불가능하게 하는 효과도 있습니다.
모든 생성자는 명시적이든, 묵시적으로든 상위 클래스의 생성자를 호출하게 되는데, 이를 private으로 선언해 이를 막을 수 있습니다.
❤️ 스터디 질답 정리
Q. 인스턴스화를 언제 막아야 하는가?
A. 상속하지 못하도록 막아야하는 경우나, 매번 객체를 생성하지 않고 특정 메서드나 필드 사용하는 경우가 있을 것 같습니다.(ex. 유틸리티 클래스)
Q. 그럼 유틸리티 클래스는 언제 사용하는지?
A. 개인적으로는 여러 클래스에서 자주 사용해서 중복되는 코드가 있을 때, 컨벤션을 만들 때 사용합니다.
Q. 객체지향에서 유틸리티성 클래스가 왜 안티패턴일까?
A. 강결합이 되고, 객체지향적이지 못하기 때문
Q. 유틸리티 클래스를 만들 때 인스턴스화를 막는 가장 좋은 방법?
A. 추상 클래스를 사용한다. (기본 생성자는 열어둠)
추가 학습 후 내 생각
이펙티브 자바에서는 추상클래스를 사용하면 안된다고 명시했다. 그런데 위의 언급에서 보듯 스프링에서는 abstract 클래스를 사용하고 기본 생성자를 private으로 하지 않아 public 생성자가 컴파일 시점에 자동 생성된다. 하지만 스프링 코드스타일에서 유틸리티 클래스는 abstract로 만들고 private으로 생성자를 명시하라고 적혀있다. 이것에 대해서 조금 의문이고, 어떤게 정답인지 스프링 코드스타일이 업데이트가 안된건지 확인이 필요할 것 같다.
개인적인 생각으로는 이펙티브 자바에서 말하는 것 처럼 private 생성자를 활용하는게 한눈에 무슨 역할을 하는지 판단이 되어 더 좋아 보인다.
※한번 더 추가 : 찾아보니, 스프링에서는 아래와 같이 유틸리티 클래스를 상속해도, abstract class의 static method이기 때문에(자바에서 abstract 클래스에 static 메서드를 선언하면 객체 인스턴스에 선언되는게 아닌 클래스 자체에 선언됨 즉 override 불가능) 상속을 하여도 별 상관없다는 스탠스를 취하는 것 같다.(그럼 스프링 코드스타일좀 바꾸지....)
Q. 추상클래스를 사용하면 한눈에 의도를 파악하기가 힘들 것 같은데 왜 스프링에서는 abstract class를 사용하는 방식으로 변경했다고 생각하는지?
A. 내부적인 컨벤션 인 것 같습니다. 의도가 한눈에 안보이는 문제는 컨벤션 룰로 정해두고 사용하면 해결된다고 생각합니다.
'Study > 이펙티브 자바' 카테고리의 다른 글
[Effective Java 3E] 불필요한 객체 생성을 피하라 (0) | 2023.08.19 |
---|---|
[Effective Java 3E] 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2023.08.19 |
[Effective Java 3E] private 생성자나 열거 타입으로 싱글턴임을 보증하라 (0) | 2023.08.13 |
[Effective Java 3E] 생성자에 매개변수가 많다면 빌더를 고려하라 (0) | 2023.08.13 |