아이템 1. 생성자 대신 정적 팩터리 메소드를 사용할 수 없는지 생각해보라.
01. 생성자 대신 정적 팩터리 메소드를 사용할 수 없는지 생각해보라.
- 장점 :
- 생성자와는 달리 정적 팩터리 메서드에는 이름이 있다.
예를 들어, 소수(prime)일 가능성이 높은
BigInteger
객체를 생성하는 생성자BigInteger(int, int, random)
는BigInteger.probablePrime
과 같은 정적 팩터리 메서드 이름으로 표현하면 가독성(readability)이 더 좋아 진다. - 생성자와는 달리 호출할 때마다 새로운 객체를 생성할필요는 없다는 점이다.
변경 불가능 클래스라면 이미 만들어 둔 객체를 활용할 수도 있고, 만든 객체를 캐시(cache) 해놓고 재사용하여 같은 불필요하게 거듭 생성되는 일을 피할수도 있다. 이 기법은 경량(Flyweight)패턴과 유사하다. 동일한 객체가 요청되는 일이 잦고, 특히 객체를 만드는 비용이 클때 적용하면 성능을 크게 개선할 수 있다. - 생성자와는 달리 반환값 자료형의 하위 자료형 객체를 반환할 수 있다는 것이다. 이 유연성을 활용하면 public으로 선언되지 않은 클래스의 객체를 반환하는 API를 만들수 있다. 그러면 구현 세부사항을 감출 수 있으므로 아주 간결한 API가 가능하다. 이 기법은 인터페이스 기반 프레임워크(interface-based framework)구현에 적합한데 이 프레임워크에서 인터페이스는 정적 팩터리 메서드의 반환값 자료형으로 이용된다.
- 형인자 자료형(parameterized type) 객체를 만들 때 편하다는 점이다. ```java
// 단순 생성자를 사용하면 아래와 같이, 형 인자 자료형을 표현하는 것이 불편하다. Map<String, Map<String,List
>> m = new HashMap<String, Map<String,List >>(); 이 처럼 자료형 명세를 중복하면, 형인자가 늘어남에 따라 길고 복잡한 코드가 만들어진다. 하지만, 정적 택터리 메서드를 사용하면 컴파일러가 형인자를 스스로 알아 내도록 할 수 있다. 이런 기법을 자료형 유추(type inference)라고 부른다. ```java public static <K, V> HashMap<K,V> newInstance(){ return new HashMap<K,V>(); } Map<String, Map<String,List<String>>> m = HashMap.newInstance(); // java 1.7 부터는 Map<String, Map<String,List<String>>> m = new HashMap<>(); // 위와 같이 <>(다이아몬드 연산자)가 지원됨
- 생성자와는 달리 정적 팩터리 메서드에는 이름이 있다.
예를 들어, 소수(prime)일 가능성이 높은
- 단점 :
- 가장 큰 문제는, public이나 protected로 선언된 생성자가 없으므로 하위 클래스를 만들수 없다는 것이다. 예를 들어, 자바의 컬렉션(Collections)프레임워크에 포함된 기본 구현 클래스들의 하위 클래스는 만들수 없다. 하지만, 그래서 더 좋다는 사람도 있는데, 계승(inheritance) 대신 구성(composition) 기법을 쓰도록 장려한다는 이유에서다.
- 정적 팩터리 메서드가 다른 정적 메서드와 확연히 구분되지 않는다는 것이다.
API 문서를 보면 생성자는 다른 메서드와 뚜렷이 구별되지만, 정적 팩터리 메서드는 그렇지 않다.
JavaDoc에서 표현이 모호하기 때문에, 지금으로서는 클래스나 인터페이스 주석(comment)을 통해서 정적 팩터리 메서드임을 널리 알리거나,
정적 팩터리 메서드 이름을 지을 때 조심하는 수 밖에 없다.
보통 정적 팩터리 메서드의 이름으로는 다음과 같은 것들을 사용한다.- valueOf: 단순하게 말하자면, 인자로 주어진 값과 같은 값을 갖는 객체를 반환한다는 뜻이다. 따라서 이런 정적 택터리 매서드는 형변환(type-conversion)메서드이다.
- of: valueOf를 더 간단하게 쓴 형태임
- getInstance: 인자에 기술된 객체를 반환하지만, 인자와 같은 값을 갖지 않을 수도 있다. 싱글턴 패턴을 따를 경우, 이 메서드는 인자 없이 항상 같은 객체를 반환한다.
- newInstance: getInstace와 같지만, 호출할때 마다 다른 객체를 생성하여 반환함
- getType: getInstance와 같지만, 반환될 객체의 클래스와 다른 클래스에 팩터리 메서드가 있을 때 사용된다. Type은 팩터리 메서드가 반환할 객체의 자료형이다.
- getType: newInstance와 같지만, 반환될 객체의 클래스와 다른 클래스에 팩터리 메서드가 있을 때 사용된다. Type은 팩터리 메서드가 반환할 객체의 자료형이다.
요약
정적 팩터리 메스드와 public 생성자는 용도가 서로 다르며, 그 차이와 장단점을 이해하는 것이 중요하다. 정적 팩터리 메서드가 효과적인 경우가 많으니, 정적 팩터리 메서드를 고려해 보지도 않고 무조건 public 생성자를 만드는 것을 삼가하자.