아이템4. 인스턴스화를 막으려거든 private 생성자를 사용하라
가끔 단순히 정적 메서드와 정적 필드만을 담은 클래스를 사용하면 유용할 때가 있다.
대표적으로 java.lang.Math, java.util.Arrays 와 같이 기본 타입 값이나 배열에 관련된 메서드들을 모아 놓는 유틸리티 클래스들을 사용할 수 있다.
또한 java.util.Collections 와 같이 특정 인터페이스를 구현하는 객체를 생성해 주는 정적 팩터리 메서드를 모아 놓을 수도 있다.
이러한 유틸리티 클래스들은 따로 인스턴스 변수나 메서드가 없어도 사용할 수 있으므로 생성자가 필요하지 않다.
하지만 생성자를 명시하지 않으면 컴파일러가 자동으로 기본 생성자를 만들어 준다. 즉, 매개변수를 받지 않는 public 생성자가 만들어지며, 사용자는 이 생성자가 자동 생성된 것인지 구분할 수 없다.
public class Dog {
public static String bark(String sound) {
return sound;
}
}
static 메서드만 가지고 있는 클래스가 있다.
public static void main(String[] args) {
String bark = Dog.bark();
}
위와 같이 static 메서드를 호출하여 사용한다.
인스턴스를 만들어 사용할 수도 있지만 권장하는 방법은 아니다.
public static void main(String[] args) {
Dog dog = new Dog();
dog.bark();
}
문법적인 문제는 되지 않지만 권장하지 않는 방법이다. 또한 해당 메서드가 인스턴스 메서드인지 스태틱 메서드인지 헷갈리게 만든다.
그러니 애초에 인스턴스를 만드는 걸 방지 하도록 하자.
이를 막기 위해 추상 클래스로 만드는 경우가 있는데, 추상 클래스로는 인스턴스화를 막을 수 없다. 하위 클래스를 만들어 인스턴스화하면 그만이기 때문이다. 아래처럼 말이다.
//추상 클래스
public abstract class Dog {
public static String bark(String sound) {
return sound;
}
}
//추상 클래스 Dog를 상속받은 하위 클래스
public class BichonFrise extends Dog{
public static void main(String[] args) {
Dog dog = new Dog();
dog.bark();
}
}
위와 같이 인스턴스를 생성할 수 있게 된다.
다행히도 인스턴스화를 막는 방법은 아주 간단하다.
컴파일러가 기본 생성자를 만드는 경우는 오직 명시된 생성자가 없을 때뿐이니 private 생성자를 추가하면 클래스의 인스턴스화를 막을 수 있다.
public abstract class Dog {
private Dog() {
}
public static String bark(String sound) {
return sound;
}
//클래스 내부에서는 인스턴스 생성가능
public static void main(String[] args) {
Dog dog = new Dog();
}
}
위처럼 기본 생성자의 접근 제어자가 private면 클래스 외부에서 접근할 수 없다.
하지만 private 생성자를 사용해도 내부에서는 인스턴스를 만들 수 있다.
이러한 경우를 방지하기 위해 아래와 같은 방법을 사용한다.
public abstract class Dog {
private Dog() {
throw new AssertionError(); //내부에서도 인스턴스 생성시 예외 처리
}
public static String bark(String sound) {
return sound;
}
public static void main(String[] args) {
Dog dog = new Dog(); //예외 발생
}
}
기본 생성자가 내부에서 호출된 경우 예외를 터트려 막아두었다.
단점은 생성자가 보이는데 생성자를 만들면서까지 못쓰게 하는게 아이러니하다. 즉, 사용하지 못하는 코드를 만든 셈이다.
그래서 이러한 private 생성자는 사용하는 입장에서 생성자를 이해하기 어려울 수 있으므로 주석을 달아주는 것도 좋다.
public abstract class Dog {
/**
* 이 클래스는 인스턴스 생성 불가
*/
private Dog() {
throw new AssertionError();
}
public static String bark(String sound) {
return sound;
}
}
참고로 생성자를 private으로 막으면 상속을 불가능하게 하는 효과도 있다.
참고 블로그 문서
https://pro-dev.tistory.com/104
이펙티브 자바 아이템 4 - 인스턴스화를 막으려거든 private 생성자를 사용하라 - 핵심 정리
이 글은 백기선 님의 이펙티브 자바 강의와 이펙티브 자바 3 / E 편을 참고하여 작성하였습니다. 아이템 4 - 인스턴스화를 막으려거든 private 생성자를 사용하라 - 핵심 정리 인스턴스를 만들지 않
pro-dev.tistory.com
https://steady-coding.tistory.com/621
'도서 > 정리' 카테고리의 다른 글
[이펙티브 자바] 아이템6. 불필요한 객체 생성을 피하라 (0) | 2023.05.15 |
---|---|
[이펙티브 자바] 아이템5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2023.05.09 |
[이펙티브 자바] 아이템3. private 생성자나 열거 타입으로 싱글턴임을 보증하라 (0) | 2023.05.05 |
[이펙티브 자바] 아이템2. 생성자에 매개변수가 많다면 빌더를 고려하라 (0) | 2023.05.04 |
[이펙티브 자바] 아이템1. 생성자 대신 정적 팩토리 메서드를 고려하라 (0) | 2023.05.03 |