본문으로 바로가기
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.



클래스를 통해 객체를 만드는 일반적인 방법은 public으로 선언된 생성자를 사용하는 것이다. 그러나 반드시 알고 있어야 하는 또 하나의 다른 방법은 클래스에 public으로 선언된 정적 팩토리 메서드를 추가하는 것이다. 


아래 예제는 기본타입 boolean의 값을 Boolean 객체에 대한 참조로 반환한다.


1
2
3
4
public static Boolean valueOf(boolean b) {
    
    return b ? Boolean.TRUE : Boolean.FALSE;
}



클래스를 정의할 때 생성자 대신 정적 팩토리 방식으로 메서드를 제공할 수 있다. 생성자 대신 정적 팩터리 메서드를 사용하면 어떤 장점이 있을까 ? 


정적 팩터리 메서드의 장점


첫 번째 장점은, 생성자와는 달리 정적 팩터리 메서드에는 이름(name)이 있다. 생성자에 전달되는 인자들은 어떤 객체가 생성되는지를 설명하지 못하지만, 정적 팩터리 메서드는 이름을 잘 짓기만 한다면 사용하기 쉽고, 클라이언트 코드의 가독성도 높아진다. 위의 예제에서는 valueOf 라는 이름으로 직관적이며 쉽게 알 수 있다. 아래 예제를 보면 5~7번 라인은 생성자를, 10~13라인은 static를 이용하여 객체를 생성할 수 있는 메서드를 만들었다. 메서드이기 때문에 withName이라는 이름을 가지고 있어 더 직관적으로 객체를 생성하고 반환할 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Person {
    String name;
    
    //생성자를 이용한 객체 생성
    public Person(String name) {
        this.name = name;
    }
    
    //static 메서드를 이용한 객체 생성
    public static Person withName(String name) {
        
        return new Person(name);
    }
 
    @Override
    public String toString() {
        return "Person [name=" + name + "]";
    }
}
 
public class Test {
    public static void main(String[] args) {
        Person ktko = new Person("ktko");
        Person kkt = Person.withName("kkt");
        
        System.out.println(ktko.toString());
        System.out.println(kkt.toString());
    }
}



두 번째 장점은, 생성자와는 달리 호출할 때마다 새로운 객체를 생성할 필요가 없다. 이미 만들어둔 객체를 사용할 수 있고, 만들어둔 객체를 캐시 해놓고 재사용하여 같은 객체가 불필요하게 거듭 생성되는 일을 피할 수도 있다. 정적 팩터리 메서드를 사용하면 같은 객체를 반복해서 반환할 수 있으므로 어떤 시점에 어떤 객체가 얼마나 존재할지를 정밀하게 제어할 수 있다. 그런 기능을 갖춘 클래스는 개체 통제 클래스라고 부른다. 개체 통제 클래스를 작성하는 이유는 몇 가지가 있다. 개체 수를 제어하면 싱글턴 패턴을 따를 수가 있고, 객체 생성이 불가능한 클래스를 만들 수도 있다. 변경히 불가능한 클래스의 경우 두 개의 같은 객체가 존재하지 못하도록 할 수 있다.


아래 예제는 BASIC_PERSON이라는 상수가 있고 getBasicPerson() 메서드를 호출하면 새로운 객체가 아닌 만들어진 객체를 가지고 와서 사용할 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Person {
    private static final Person BASIC_PERSON = new Person("basic");
    String name;
    
    
    //생성자를 이용한 객체 생성
    public Person() {}
    
    public Person(String name) {
        this.name = name;
    }
    
    //static 메서드를 이용한 객체 생성
    public static Person withName(String name) {
        
        return new Person(name);
    }
    
    public static Person getBasicPerson() {
        
        return BASIC_PERSON;
    }
 
    @Override
    public String toString() {
        return "Person [name=" + name + "]";
    }
}


 

세 번째 장점은, 생성자와는 달리 반환값 자료형의 하위 자료형 객체를 반환할 수 있다는 것이다. 따라서 반환되는 객체의 클래스를 훨씬 유연하게 결정할 수 있다. 리턴 타입의 하위 타입의 인스턴스를 만들어 주어도 상관 없으니, 리턴 타입은 인터페이스로 지정하고, 그 인터페이스의 구현체는 API로 노출 시키지 않지만 그 구현체의 인스턴스를 만들어 줄 수 있다는 말이다. 그 예로 java.util.Collections가 그 예에 해당한다. java.util.Collections는 45개에 달하는 인터페이스의 구현체의 인스턴스를 제공하지만 그 구현체들은 전부 non-public이다. 즉 인터페이스 뒤에 숨어있고, 그 결과로 public 으로 제공해야 할 API를 줄였을 뿐 아니라 개념적인 무게 까지 줄일 수 있었다. 개념적인 무게란 프로그래머가 어떤 인터페이스가 제공하는 API를 사용할 때 알아야 할 개념의 개수와 난이도를 말한다.


네 번째 장점은, 형인자 자료형 객체를 만들 때 편하다는 점이다. 


1
Map<StringString> map = new HashMap<StringString>();



위처럼 자료형 명세를 중복하면, 혀인자가 늘어남에 따라 길고 복잡한 코드가 만들어진다. 하지만 정적 팩터리 메서드를 사용하면 컴파일러가 형인자를 스스로 알아내도록할 수 있다. 이런 기법을 자료형 유추(type interference)라고 부른다. 아래와 같이 메서드가 있으면 좀 더 쉽게 간결하게 만들 수 있다.


1
2
3
4
public static <K, V> HashMap<K, V> newInstance() {
    
    return new HashMap<K,V>();
}



주의할 것은 1.7버전 부터는 생성자를 호출할 때도 자료형 유추를 사용할 수 있다. 생성자에 자료형 유추가 가능해졌으므로, 표준 컬렉션 메서드에 정적 팩터리 메서드를 추가할 필요가 없어졌다. 아래 코드와 같이 1.7 버전 이상이라면 아무 문제 없이 동작한다. 4번째 장점은 장점이 아닌게 되버렸다.


1
Map<StringString> map = new HashMap<>();



정적 팩터리 메서드의 단점


첫 번째 단점은 public이나 protected로 선언된 생성자가 없으므로 하위 클래스를 만들 수 없다. 

두 번째 단점은 정적 팩터리 메서드가 다른 정적 메서드와 확연히 구분되지 않는다는 것이다.

API문서를 보면 생성자는 다른 메서드와 또렷이 구별되지만, 정적 팩터리 메서든느 그렇지 않다. 그러니 생성자 대신 정적 팩터리 메서드를 통해 객체를 만들어야 하는 클래스는 사용법을 파악하기가 쉽지 않다. JavaDoc이 언젠가는 정적 팩터리 메서드를 다른 메서드와 다르게 표시해주는 날이 올 지도 모르지만, 지금으로서는 클래스나 인터페이스 주석을 통해 정적 팩터리 메서드임을 알리거나, 정적 팩터리 메서드 이름을 지을 때 조심하는 수 밖에 없다.



정적 팩토리에 대해서 어느정도 알 수 있었지만 3번 째 장점에 대해서는 쉽게 이해하기가 힘들다.

백기선님의 유튜브를 보면 장점을 5가지로 분류해놓으셨는데 필요하다면 한번 보는 것도 추천한다.