[Java] 객체를 생성하는 대표적인 방법, 정적 팩토리 메소드

wakeup Lee·2023년 3월 15일
0

자바

목록 보기
1/6
post-thumbnail

객체를 생성할 수 있는 방법에는 대표적으로 생성자, 정적 팩토리 메소드, 빌더패턴이 존재한다. 그 중에서 정적 팩토리 메소드에 대해서 학습한 내용을 정리하려 한다.

이펙티브 자바(Epective Java 3/E)에서는 정적 팩토리 메소드를 다음과 같이 설명하고 있다.

1. 정적 팩토리 메소드는 생성자와 달리 이름을 가질 수 있다.

  • 생성자
public class User {
	
    String userName;
    String userId;
    
    public User(String userName, String userId) {
    	this.userName = userName;
        this.userId = userId;
    }
}

생성자는 new 인스턴스로 객체를 생성한다.

User user = new User("홍길동", "gildong");
  • 정적 팩토리 메소드
public class User {
	
    String userName;
    String userId;
    
    public static User createUser(String userName, String userId) {
    
    	User user = new User();
        user.userName= userName;
        user.userId = userId;
        
    	return user;
    }
}

정적 팩토리 메소드는 createUser()라는 직관적인 이름을 가진 메소드를 호출하여 객체를 생성한다.

User user = User.createUser("홍길동", "gildong");

❗new User()로 객체를 생성하는 것보다 createUser()라는 메소드 이름을 지어 객체를 생성하는 것이 훨씬 가독성이 좋다는 것을 알 수 있다.

2. 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.

생성자를 사용할 때는 new 연산자를 사용하기 때문에 여러번 호출하게 되면 매번 새로운 인스턴스가 생성되게 된다. 하지만 정적 팩토리 메소드를 사용하면 static의 특징인 프로그램 로딩부터 종료시까지 하나의 메모리에만 할당되어 같은 객체를 자주 요청하는 상황이 생긴다면 생성자보다 성능을 끌어올릴 수 있다.

3. 반환 타입의 하위 객체를 반환할 수 있는 능력이 있다.

대표적으로 BigInteger클래스를 정적 팩토리 메소드의 예로 들 수 있다. BigInteger의 valueOf 메소드를 보면 아래와 같이 구현되어 있다.

    /**
     * Returns a BigInteger whose value is equal to that of the
     * specified {@code long}.
     *
     * @apiNote This static factory method is provided in preference
     * to a ({@code long}) constructor because it allows for reuse of
     * frequently used BigIntegers.
     *
     * @param  val value of the BigInteger to return.
     * @return a BigInteger with the specified value.
     */
    public static BigInteger valueOf(long val) {
        // If -MAX_CONSTANT < val < MAX_CONSTANT, return stashed constant
        if (val == 0)
            return ZERO;
        if (val > 0 && val <= MAX_CONSTANT)
            return posConst[(int) val];
        else if (val < 0 && val >= -MAX_CONSTANT)
            return negConst[(int) -val];

        return new BigInteger(val);
    }
    
    public static final BigInteger ZERO = new BigInteger(new int[0], 0);
    private static BigInteger posConst[] = new BigInteger[MAX_CONSTANT+1];
    private static BigInteger negConst[] = new BigInteger[MAX_CONSTANT+1];

4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.

반환 타입의 하위 타입이기만 하면 어떤 클래스의 객체를 반환하든 상관없다.
EnumSet 클래스의 noneOf 메소드를 보면 원소의 수가 64개 이하면 long 변수 하나로 관리하는 RegularEnumSet의 인스턴스를, 65개 이상이면 long배열로 관리하는 JumboEnumSet의 인스턴스를 반환한다. 여기서 중요한 것은 클라이언트는 이 두 클래스의 존재를 모른다.

    /**
     * Creates an empty enum set with the specified element type.
     *
     * @param <E> The class of the elements in the set
     * @param elementType the class object of the element type for this enum
     *     set
     * @return An empty enum set of the specified type.
     * @throws NullPointerException if {@code elementType} is null
     */
    public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
        Enum<?>[] universe = getUniverse(elementType);
        if (universe == null)
            throw new ClassCastException(elementType + " not an enum");

        if (universe.length <= 64)
            return new RegularEnumSet<>(elementType, universe);
        else
            return new JumboEnumSet<>(elementType, universe);
    }

하지만 이런 정적 팩토리 메소드에도 단점이 존재한다.

  1. 상속을 하려면 public이나 protected 생성자가 필요하니 정적 팩토리 메소드만 제공하면 하위 클래스를 만들 수 없다.
    • 정적 팩토리 메소드만 제공하여 private로 생성자를 생성하게 되는 경우 하위 클래스 생성이 불가하다.
  2. 정적 팩토리 메소드는 프로그래머가 찾기 어렵다.
    • 생성자처럼 API 설명에 명확히 드러나지 않아 사용자는 정적 팩토리 메소드 방식의 클래스를 인스턴스화할 방법을 찾아야한다.

[참고자료]
이펙티브 자바

profile
백엔드 개발자

0개의 댓글