본문으로 건너뛰기
홈
기술
기술 전체
프로그래밍68
컴퓨터 과학63
AI48
웹 개발36
인프라33
데이터31
소프트웨어 공학18
소개
← 목록으로프로그래밍 › Java › 기초

규칙 1. 생성자대신 정적 팩터리 메서드를 사용할 수 없는지 생각해 보라

목차

규칙 1. 생성자 대신 정적 팩터리 메서드를 고려하기

객체를 만드는 가장 단순한 방법은 생성자다. 정적 팩터리 메서드(static factory method)는 이름을 가진 생성 경로를 제공한다. 항상 생성자보다 낫다는 뜻은 아니다. 같은 인자형으로 여러 의미의 객체를 만들거나, 생성 전에 검증·정규화가 필요하거나, 반환 구현을 감추고 싶을 때 효과적이다.

public final class AccountId {
    private final long value;

    private AccountId(long value) { this.value = value; }

    public static AccountId of(long value) {
        if (value <= 0) throw new IllegalArgumentException("ID는 양수여야 합니다");
        return new AccountId(value);
    }

    public long value() { return value; }
}

AccountId.of(42)는 메서드 이름을 통해 검증된 ID로 만들기라는 뜻을 전달한다. 생성자를 private으로 두어 호출자가 검증을 우회하지 못하게 했다. 팩터리는 null을 반환하거나 조용히 잘못된 입력을 바꾸기보다 계약에 맞게 오류를 알린다. 다만 이렇게 만든 AccountId는 값 동등성이 필요하면 equals·hashCode도 구현해야 한다. Java의 record가 과제에 맞는다면 더 간단할 수 있다.

정적 팩터리의 네 가지 이점

  1. 이름을 붙일 수 있다. Duration.ofSeconds(5)처럼 인자의 단위를 드러낸다. 같은 인자형의 생성자를 두 개 만들 수 없어 억지로 순서를 바꾸는 실수를 피한다.
  2. 매번 새 객체를 만들 필요가 없다. 불변 값의 재사용이나 캐시는 가능하지만 반드시 캐시하는 것은 아니다. 따라서 호출자가 ==로 새 객체 여부를 가정하면 안 된다.
  3. 반환 구현을 감출 수 있다. 반환 타입이 인터페이스나 상위 타입이면 내부 구현 클래스를 바꿀 수 있다. List.of(...) 같은 API는 사용자가 구체 구현 클래스를 알 필요가 없다.
  4. 인자에 따라 다른 구현을 선택할 수 있다. 작은 값과 큰 값, 저장 방식이 다른 경우에도 공개 반환 타입을 유지할 수 있다. 대신 선택 규칙이 호출자에게 중요한 계약이라면 문서화해야 한다.

원문에는 “인터페이스는 정적 메서드를 가질 수 없다”고 적혀 있으나 현재 Java의 인터페이스에는 정적 메서드를 선언할 수 있다. 또한 제네릭의 new HashMap<String, List<String>>() 반복 표기는 다이아몬드 연산자 new HashMap<>()로 줄일 수 있어, 형인자 추론만을 위해 별도 정적 팩터리를 만들 필요는 적다. 오래된 책의 예시를 읽을 때는 이 두 언어 변화에 주의한다.

선택 기준과 단점

질문생성자정적 팩터리
만드는 방식이 한 가지이고 단순한가읽기 쉬움메서드 하나가 오히려 우회일 수 있음
같은 인자로 다른 의미가 있는가이름을 붙이기 어려움from, of, parse처럼 구분 가능
불변 값 재사용이 필요한가보통 새 객체 생성재사용 선택 가능
하위 클래스를 열어야 하는가접근 가능한 생성자 필요생성자를 모두 숨기면 외부 상속 불가

표의 마지막 행은 설계 의도를 결정한다. 모든 생성자를 비공개로 만들면 하위 클래스가 부모 생성자를 호출할 수 없다. 확장 지점을 제공해야 한다면 protected 생성자나 인터페이스를 검토한다. 정적 팩터리만 공개하면 IDE와 문서에서 객체 생성 경로를 찾기 어려울 수 있으므로 of, from, valueOf, getInstance처럼 역할이 분명한 이름과 예제를 제공한다.

정적 팩터리는 서비스 제공자 구조에도 활용할 수 있다. 다만 원문의 간단한 레지스트리 코드는 공개 생성자를 “객체 생성 방지”라고 표시하는 등 구현 오류가 있었다. 플러그인을 찾는 목적이라면 현대 Java의 ServiceLoader 같은 표준 도구를 먼저 검토하고, 고유 레지스트리가 필요한 경우 등록·중복 이름·실패 정책을 명시한다.

참고: Oracle Java Language Specification: Interface Members, ServiceLoader API, List.of API

같은 카테고리의 글