[Java] 인스턴스화 방지를 위해 private 생성자 이용하기
정적 메소드, 정적 필드만 담은 클래스는 때때로 유용하게 쓰일 수 있다.
사용하는 경우가 크게 세 가지가 있는데 예제를 통해 살펴보자.
📕 1. 기본 타입 값이나 배열 관련 메소드를 모은 클래스
ex: java.lang.Math, java.util.Arrays
java.util.Arrays의 코드를 살펴보면 아래와 같다.
public class Arrays {
public static boolean isArray(Object o) { ...}
public static Object[] asObjectArray(Object array) { ...}
public static List<Object> asList(Object array) { ...}
public static <T> boolean isNullOrEmpty(T[] array) { ...}
// ...
private Arrays() {
}
}
우리가 Arrays를 사용할 때를 생각해보면 절대 Arrays arrays = new Arrays()로 생성하지 않는다.
Arrays.asList(배열) 식으로 바로 사용한다.
내부 메소드를 보니 전부 static으로 선언되어 있고 생성자가 private로 선언된 것을 확인할 수 있다.
📒 2. 특정 인터페이스를 구현하는 객체를 생성해주는 정적 메소드(or 팩토리)를 모은 클래스
ex: java.util.Collections
java.util.Arrays와 마찬가지로 static으로만 구성, private 생성자 존재하는 것을 확인할 수 있다. (생략함)
📘 3. final 클래스와 관련된 메소드들을 모아놓은 클래스
final 클래스는 보안 상의 이유로 상속을 금지 시킨다.
(final 클래스를 상속해 하위 클래스를 만드는 행동이 시스템의 파괴를 야기할 수 있음)
대표적인 예로는 String 클래스가 있다.
우리는 아무도 String 클래스를 상속 받아 사용하지 않는다. (막혀있기도 하지만)
많은 사람들이 다양하게 사용하고 있기에 막 상속 받아 쓰다가 어떤 문제가 일어날지 감히 예상조차 힘들다.
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { ... }
이러한 final 클래스와 관련된 메소드들을 모아둔 클래스가 존재한다면?
예제용으로 junit에 존재하는 StringUtils를 가져와보았다.
@API(status = INTERNAL, since = "1.0")
public final class StringUtils {
private static final Pattern ISO_CONTROL_PATTERN = compileIsoControlPattern();
private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s");
static Pattern compileIsoControlPattern() { ... }
private StringUtils() {
/* no-op */
}
public static boolean isBlank(String str) { ... }
public static boolean isNotBlank(String str) { ... }
public static boolean containsWhitespace(String str) { ... }
// ...
}
마찬가지로 static으로 선언되어 바로 호출할 수 있다.
👉
정적 메소드를 담은 클래스들은 보통 유틸리티 클래스로 사용하기 위해 생성된다.
유틸리티 클래스는 기본적으로 인스턴스로 만들어 쓰려고 설계한 것이 아니다.
크게 세 가지로 구분해두었지만 모든 메소드가 static으로 이루어진 유틸리티 클래스에서 사용하면 된다.
생성자를 생성하지 않는다면?
❓👉 생성자를 명시하지 않으면 컴파일러가 자동으로 public으로 기본 생성자가 생성해준다.
사용자가 코드만 보고 생성자가 없다고 생각하더라도 컴파일 시 자동으로 생성된다.
추상 클래스로 만들어서 인스턴스화를 막기?
❓abstract 클래스는 인스턴스로 생성하는 것이 불가능하다.
private 생성자 대신에 쓸 수 있지 않을까? 라는 생각이 들 수 있다.
👉 하위 클래스를 생성하면 인스턴스화가 가능해진다.
👉 또한 abstract 클래스는 보통 클래스들의 공통 필드와 메소드를 정의하는 목적으로 만들기 때문에 적절하지 않다. 상속해서 사용하라는 의미로 오해할 수 있다.
참고
- Effective Java / 조슈아 블로크 / 26p ~ 27p
- https://www.tutorialspoint.com/why-string-class-is-immutable-or-final-in-java