객체를 생성할 수 있는 방법에는 대표적으로 생성자, 정적 팩토리 메소드, 빌더패턴이 존재한다. 그 중에서 정적 팩토리 메소드에 대해서 학습한 내용을 정리하려 한다.
이펙티브 자바(Epective Java 3/E)에서는 정적 팩토리 메소드를 다음과 같이 설명하고 있다.
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()라는 메소드 이름을 지어 객체를 생성하는 것이 훨씬 가독성이 좋다는 것을 알 수 있다.
생성자를 사용할 때는 new 연산자를 사용하기 때문에 여러번 호출하게 되면 매번 새로운 인스턴스가 생성되게 된다. 하지만 정적 팩토리 메소드를 사용하면 static의 특징인 프로그램 로딩부터 종료시까지 하나의 메모리에만 할당되어 같은 객체를 자주 요청하는 상황이 생긴다면 생성자보다 성능을 끌어올릴 수 있다.
대표적으로 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];
반환 타입의 하위 타입이기만 하면 어떤 클래스의 객체를 반환하든 상관없다.
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);
}
[참고자료]
이펙티브 자바