이 글은 Effective Java 3판을 읽고 정리한 글 입니다!
정적 팩터리 메서드의 대표적 예시인 LocalDate.of
입니다.
정적(static)으로 클래스의 인스턴스를 반환하기 때문에 정적 팩터리 메서드라 부릅니다.
생성자도 있는데, 왜 이런 메서드를 사용할까요?
책의 예제에서 다루는 BigInteger.probablePrime
의 예를 보겠습니다.
BigInteger.probablePrime
는 주어진 비트로 만들 수 있는 수 중에 랜덤한 소수를 반환하는 정적 팩터리 메서드 입니다.
만약 일반적인 public BigInteger(int bigLength, Random rnd)
생성자라면 무엇을 생성하는지 유추할 수 없을 것 입니다.
하지만, probablePrime
처럼 메서드 명으로 어떤 특성을 갖는지 설명할 수 있으면 사용하는 사람도 편하고, 유지보수하는 사람도 편하겠죠?
인스턴스를 미리 만들어놓거나, 새로 생성한 인스턴스를 캐싱하여 재활용하는 방식으로 사용될 수 있습니다.
가장 대표적인 예시로 싱글톤 패턴이 있습니다.
public class Singleton {
// 1. 최초에 한번만 만들어 놓음
private static Singleton instance = new Singleton();
private Singleton() {
// 생성자는 외부에서 호출못하게 private 으로 지정해야 한다.
}
// 정적 팩터리 메서드로 미리 만들어놨던 1번의 인스턴스를 반환
public static Singleton getInstance() {
return instance;
}
이렇게 언제, 어느 인스턴스를 살아 있게 할지를 정할 수 있습니다. 이런 클래스를 인스턴스 통제 클래스라고 부릅니다.
이런 특징은 저희가 자주 사용하는 Enum
클래스의 근간이 됩니다.
Enum 생성자 위에 javadoc을 번역하면...
단독 프로그래머가 이 생성자를 호출할 수 없습니다. 이것은 열거형 선언에 대한 응답으로 컴파일러에 의해 사용된 코드에 의해 사용된다.
🚨 emit에 대한 의미가 애매해서 의역했습니다. emit에 대한 답변은 스택오버플로우 링크에서 참고해주세요.
대강 프로시저 코드에서 ~를 사용해서 만든 무언가란 의미인 것 같습니다.
생성자를 protected
로 만든 뒤, valueOf
로 값을 얻게 만들고 있습니다.
인스턴스화 할 수 없는 Enum
의 특징을 잘 보여주고 있습니다.
책에서는 저희가 많이 사용하는 Collections 클래스의 예제를 들고 있습니다.
컬렉션 프레임워크는 수정 불가, 동기화 지원 등 45개의 유틸리티 클래스 구현체가 있다고 합니다.
이렇게 많은 구현체를 Collections
에서 정적 팩토리 메서드로 반환하고 있습니다.
이렇게 반환한 클래스들은 개발자들이 구체 클래스를 상세하게 뜯어 공부할 수고를 줄여줍니다. 명시한 인터페이스의 하위 타입 이므로, 인터페이스만으로 조작이 가능하기 때문입니다.
EnumSet
은 원소가 64개 이하면 원소들을 long
변수 하나로 관리하는 RegularEnumSet
의 인스턴스를, 65개 이상이면 long
배열로 관리하는 JumboEnumSet
의 인스턴스를 반환합니다.
이처럼 입력 변수에 따라 하위타입의 인스턴스를 반환할 수 있습니다.
만약 원소가 0개인 EnumSet
을 따로 처리하고 싶어서 EmptyEnumSet
클래스를 따로 만든다고 하면, 그냥 상속받고 정적 팩터리 메서드에 길이가 0 이면 EmptyEnumSet
을 반환한다는 조건하나만 추가하면 됩니다.
이 부분은 이해가 더 필요할 것 같아서, JDBC 파트를 따로 작성해 연구해보겠습니다.
생성자를 private으로 막고, 정적 팩터리 메서드만을 통해 생성을 하도록 만든다면 상위 클래스를 생성할 수 없기 때문에 상속이 불가능합니다.
하지만!! 이 부분은 불변 타입으로 만들기 위해선 오히려 장점으로 작용할 수도 있습니다.
이 부분은 IntelliJ를 사용한다면 크게 와닿지 않는 단점이라 생각합니다(개인적인 의견입니다.)
조금 불편한건 기본적인 생성자는 단축키로 3초안에 만들 수 있지만, 정적 팩터리 메서드는 직접 이름을 지어줘서 만들어줘야 하기 때문에 생각과 고민이 필요한 점이 있겠습니다.
from
매개변수를 하나 받아서 해당 타입의 인스턴스를 반환하는 형변환 메서드
ex)
Date d = Date.from(instant);
of
여러 매개변수를 받아 적합한 타입의 인스턴스를 반환하는 집계 메서드
ex)
Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
valueOf
from과 of의 더 자세한 버전
ex)
BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
instance
, getInstance
매개변수로 명시한 인스턴스를 반환하지만, 같은 인스턴스임을 보장하지는 않음.
ex)
StackWalker luke = StackWalker.getInstance(options);
create
, newInstance
위 instance
, getInstance
와 비슷하나, 매번 새로운 인스턴스를 생성해 반환
ex)
Object newArray = Array.newInstance(classObject, arrayLen);
getType
생성할 클래스가 아닌 다른 클래스의 팩터리 메서드를 정의할 때 쓴다.
ex)
FileStore fs = Files.getFileStore(path)
newType
생성할 클래스가 아닌 다른 클래스의 팩터리 메서드를 정의할 때 쓴다. 항상 새로운 인스턴스를 생성해 반환한다.
ex)
BufferedReader br = Files.newBufferedReader(path);
type
좀 더 간결한 버전
ex)
List<Complaint> litany = Collections.list(legacyLitany);
읽고 채화하는데 오래걸리지만, 정말 재밌어요 ㅎㅎ
여러분들도 꼭 읽어보셨으면 좋겠습니다.
이펙티브 자바 3판