2270 단어
11 분
[#1] 이펙티브 자바 (정적 팩토리 메서드)

자바 개발자라면 한번쯤은 읽어봐야하는 책이라고 하는 이펙티브 자바입니다.

책 구매는 8월 쯔음에 한거같은데, 프로젝트 1개가 어느 정도 완성된 거 같아 슬슬 하나씩 읽어보면서 코드 좀 갈아보려고요.

이거 외에도 토비의 스프링이나 종만북, 노랭이도 일단 있긴한데 그닥 손이 안가는 거 보면 당장 필요한 시기는 아닌가봅니다.

TIP

미리 말하지만 부정안하고 제가 책 읽는 것을 진짜 싫어합니다.

  • 제가 알고리즘 공부하려고 종만북 샀던 것도 1개 읽고 1개는 그대로 방치된 상태입니다.
  • 너무 방치하기엔 아까워서 개인 프로젝트 완수한 시점에 읽으면 좋아보이더라고요

정적 팩토리 메서드 (Static Factory Method)#

  • 객체의 생성을 담당하는 클래스 메서드입니다.

  • 클래스의 인스턴스를 얻는 전통적 수단 public 생성자

  • 또 알아야 할 게 있는데 그게 정적 팩터리 메서드

장점

  • 이름을 가질 수 있음
  • 호출될 때마다 인스턴스 새로 생성할 필요 없음
  • 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있음
  • 입력 매개변수에 따라 매번 다른 클래스 객체 반환할 수 있음
  • 정적 팩터리 메서드를 작성하는 시점에 반환 객체의 클래스가 존재하지 않아도 됨

단점

  • 상속하려면 public이나 protected 생성자가 필요해 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없음
  • 정적 팩터리 메서드는 프로그래머가 찾기 어려움

사실 뭔 소린지 모르겠습니다#

책 잘 읽고 똑똑하면 서울대갔지.. 제 머리는 암기하는 머리가 아니기에 우선 키워드만 땄습니다

머리가 좋지 않으니 하나하나 파서 해보죠

public 생성자#

public은 알겠는데 왜 public 생성자인지부터 생각해야합니다.

public class Person {
private String name;
public Person() { }
private Person(String name) {
this.name = name;
}
}
  • 일단 public 생성자는 클래스 바깥에서 객체를 만들 수 있는 공개되어 있는 생성자를 말합니다.
Person p = new Person();

우리는 내부에 Person이라는 것이 있기에 생성자를 이해했습니다.

그렇다면 public 생성자는?

  • 앞에 public이 붙으면 public 생성자가 됩니다.
  • 반대로 private이 붙으면 private 생성자가 되겠지요
    • 이건 싱글톤에 사용하겠네요

정적 팩토리 메서드#

그러면 또 익혀야 한다는 그 정적 팩토리 메서드는 뭘까요?

public class Person {
private String name;
private Person(String name) {
this.name = name;
}
public static Person from(String name) {
return new Person(name);
}
}

음.. 분명 저런 코드를 과거에 짜던 기억은 있지만 어째서 좋은건가요?

장점 분석#

NOTE

책의 긴 글 잘 안읽히니까 핵심 키워드만 뽑아서 하나씩 해보겠습니다

이름을 가질 수 있음#

그러네요. 이름 가질 수 있네요

// public 메서드
new Person("SY");
// 정적 팩토리 메서드
Person.from("SY");
  • 위 / 아래 쓰기엔 위가 편하겠지만, 의도 파악은 아래가 더 좋아 보입니다.
  • 예로 Letter.to("SY") 이나 Letter.from("YJ") 이런 느낌으로다가 의도에 맞게 만들 수 있겠네요.

호출될 때마다 인스턴스 새로 생성할 필요 없음#

Person p1 = new Person("SY");
Person p2 = new Person("SY");

원래라면 제가 이런식으로 계속 만들텐데

public class Person {
private static final Map<String, Person> CACHE = new HashMap<>();
private String name;
private Person(String name) {
this.name = name;
}
public static Person of(String name) {
if (CACHE.containsKey(name)) {
return CACHE.get(name);
}
Person p = new Person(name);
CACHE.put(name, p);
return p;
}
}

이러면 제 이름이 수 백명이라도 이미 있는 객체 그대로 쓰겠죠

즉, 매번 new 할 필요 없으니까 이것도 이해했습니다.

반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있음#

인터페이스 뒤에 진짜 구현체를 숨긴다 그런 소리 같은데요?

public class GradeCalculator {
public static List<Integer> getScores() {
return new ArrayList<>();
}
}

예로 반환타입은 List인데 실제로는 하위 구현체인 ArrayList로 반환하는 것처럼요

즉, 정적 팩토리 메서드에 적용하면, 굳이 API 사용하는 사람한테 클래스 알려줄 필요가 없다. 그렇 소리 같습니다.

public interface Mobile {
void call();
}
class Galaxy implements Mobile {}
class IPhone implements Mobile {}
public class MobileFactory {
public static Mobile getPhone(String Type) {
if ("Apple".equals(type)) {
return new IPhone();
}
return new Galaxy();
}
}

음, 그러니까 Mobile만 알면되는데 그게 뭔 폰인지는 알 필요 없다는 그런 거네요. 구체 클래스 이름은 가리고, 알 필요도 없어지면 확실히 좋긴 하네요.

입력 매개변수에 따라 매번 다른 클래스 객체 반환할 수 있음#

음.. 이건 아마 유연하게 할 수 있다는 그런걸까요?

잘 모르는데 대충 EnumSet이 대표라곤 하는데 코드 뜯으면서 확인해보겠습니다.

EnumSet<Color> colors = EnumSet.of(Color.RED, Color.BLUE);

일단 눈으로 보이는 건 EnumSet인데, 따로 public 생성자가 없네요? 정적 팩토리 메서드라는건 일단 바로 알긴했는데, 그래서 왜죠?

public abstract class EnumSet<E extends Enum<E>> {
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
Enum<?>[] universe = getUniverse(elementType);
if (universe.length <= 64) {
return new RegularEnumSet<>(elementType, universe);
}
return new JumboEnumSet<>(elementType, universe);
}
}

코드 중 일부 가져왔는데, 보시면 굉장히 재밌는 구조네요.

Enum 개수가 여러개든 아니든 일단 받고, 64개 이하면 RegularEnumSet을 리턴하고 그 이상은 JumboEnumSet을 리턴합니다.

또 들어가서 보면 RegularEnumSet은 long 변수 하나로 뭐 비트마스킹 써서 관리하고 있고, JumboEnumSet은 long[] 배열 써서 관리하던데 그냥 알아서 최적화해준다는 게 엄청나네요.

비트마스킹은 알고리즘 영역이고 쉽게 설명하면 이진수 표현으로 계산해서 효율 극대화한건데, 이건 웹 개발자가 현대에서 쓸 일이 거의 없기에 필요한 거 아니면 의식하지 않는 게 코딩을 재밌게 하려면 필요한 게 아니면 의식하지 않아도 될 거 같습니다.

정적 팩터리 메서드를 작성하는 시점에 반환 객체의 클래스가 존재하지 않아도 됨#

마지막은 그냥 JDBC를 알려주는 거 같네요.

Connection conn = DriverManager.getConnection("jdbc:mysql://...");

우리가 MySQL을 쓸지 PostgreSQL을 쓸지 코드 짤 때는 모를 수 있잖아요?

이건 드라이버 나중에 등록하면 알아서 Connection 객체를 만들어 준다는 거 같습니다.

단점 분석#

단점도 뜯어 봐야죠. 장점만 알아서 뭐합니까

상속하려면 public이나 protected 생성자가 필요해 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없음#

public class Parent {
private Parent() {}
public static Parent getInstance() {
return new Parent();
}
}
public class Child extends Parent {
public Child() {
super(); // Error
}
}

이건 생성자를 막아버리니 다른 클래스가 상속받을 수 없다는 거네요.

  • 뭐,, 요즘에는 안꼬이려고 다 때네려고 하는데 오히려 장점인게 아닌가 싶은데요..?

정적 팩터리 메서드는 프로그래머가 찾기 어려움#

생성자처럼 주석 달던지 문서화하던지 그럴 수 없으니 그런 거 같습니다.

일종의 규칙이 있다는 데 그거만 대충 기억하면 나도 정적 팩토리 메서드를 할 줄 안다. 가 될 거 같습니다.

Rule#

  • from : 매개변수 하나 받아서 객체 생성할 때
    • Data a = Data.from(instant);
  • of : 매개변수 여러 개 받아서 객체 생성할 때
    • Set<Rank> cards = EnumSet.of(JACK, QUEEN, KING);
  • valueOf : fromof의 더 자세한 버전
    • String s = String.valueOf(true);
  • instance, getInstance : 인스턴스를 반환하지만, 매개변수와 같은 값인지는 보장 안함
    • Calendar cal = Calendar.getInstance();
  • create, newInstance : getInstance와 같지만, 매번 새로운 객체 생성 보장
    • Object array = Array.newInstance(classObject, length);

정리#

  • 결론은 내 프로젝트 안봐도 너 public 생성자 써서 한거 다 알고있음이라고 묻는 거 같네요.
  • 머리에 넣을 게 아니라 빨리 니 코드 고치고 다음꺼 봐라라는 거 같습니다.
  • 첫장부터 이거 박아넣어서 제 코드를 뜯게 만드는 이 사람은 진짜 뭐하는 사람일까요?
  • 이거 재밌네요. Item 90개 검토하려면 최소 3개월 박겠는데 리펙토링, 성능 개선 생각하면 당분간은 자바만 생각해도 될 거 같습니다
[#1] 이펙티브 자바 (정적 팩토리 메서드)
https://devlog.jpstudy.org/posts/2025/java/effective_java/1/
저자
SY
게시일
2025-12-10
라이선스
CC BY-NC-ND 4.0