Math 클래스
public final class Math {
// Private constructor to prevent instantiation
private Math() {}
// Static utility methods
public static int add(int a, int b) {
return a + b;
}
public static double sqrt(double value) {
return java.lang.Math.sqrt(value);
}
// More utility methods...
}
- package java.lang;
- Math 클래스는 final로 선언되어 있어 상속할 수 없다.
- 생성자가 private로 선언되어 있어, 외부에서 인스턴스화를 방지
- add나 sqrt 같은 메서드들은 정적 메서드로 선언되어, Math.add(5, 10)과 같은 방식으로 호출
StringUtils 클래스
/**
* Utility class for working with Strings.
*/
public abstract class StringUtils {
// Private constructor to prevent instantiation
private StringUtils() {}
/**
* Check whether the given String is empty (""), null or only whitespace.
*
* @param str the String to check
* @return {@code true} if the String is empty or {@code null}, otherwise {@code false}
*/
public static boolean isEmpty(CharSequence str) {
return (str == null || str.length() == 0);
}
/**
* Check whether the given String has text (i.e. not null, not empty, and not only whitespace).
*
* @param str the String to check
* @return {@code true} if the String has text, otherwise {@code false}
*/
public static boolean hasText(CharSequence str) {
if (str instanceof String) {
return hasText((String) str);
}
return (str.length() > 0);
}
private static boolean hasText(String str) {
int strLen = str.length();
if (strLen == 0) {
return false;
}
for (int i = 0; i < strLen; i++) {
if (!Character.isWhitespace(str.charAt(i))) {
return true;
}
}
return false;
}
// 생략 ..
}
- package org.springframework.util;
- 여러 유틸리티 메서드들을 정적(static)으로 제공
- 인스턴스화되거나 상속될 필요가 없으므로, final로 선언되고 private 생성자를 갖는다.
유틸리티 클래스 설계
static 메서드 & static 필드를 모아둔 클래스를 만든 경우 해당 클래스를 abstract로 만들어도 인스턴스를 만드는 걸 막을 순 없다. 상속 받아서 인스턴스를 만들 수 있기 때문이다.
그러면 어떻게 해야 인스턴스화를 막을까?
2가지 방법이 있다.
1. abstract + private 생성자
public abstract class Utility {
private Utility() {
}
static int plus(int a, int b){
return a + b;
}
}
public class UtilityChild extends Utility{
public UtilityChild() {
super(); // 컴파일 에러 -> 자식 클래스 상속 불가능
}
}
- 자바에서 상속을 받으면 부모 생성자를 호출해야한다.
- 하지만 부모의 생성자가 private이면 생성자 호출을 불가능하다.
2. final + private 생성자
public final class Utility {
private Utility() {
}
static int plus(int a, int b){
return a + b;
}
}
- fianl 키워드로 상속 자체를 막는다.
- privae 생성자를 통해서 자체 인스턴스화를 막는다.
어떤 방식이 더 좋을까?
내 생각은 fianl + private 방식이 더 괜찮다고 생각한다.
final 키워드, private 생성자는 클래스의 상속과 인스턴스화를 모두 명확하게 방지
즉 설계 의도가 명확해지고, 다른 개발자들이 클래스를 사용하는 데 혼란이 없다.