✅ static factory method
- 자바에서 객체를 생성하는 한 가지 방법으로,
new
키워드를 직접 쓰지 않고, 정적 메서드를 통해 객체를 반환하는 방식
✅ 기본 예시
public class User {
private String name;
private User(String name) {
this.name = name;
}
// 정적 팩토리 메서드
public static User of(String name) {
return new User(name);
}
}
User user = User.of("홍길동"); // new User("홍길동") 대신 사용
✅ 장점 6가지 (Effective Java 3판 기준)
1. 이름을 가질 수 있다 🏷️
- 생성자보다 이름 있는 메서드가 더 의도를 잘 표현함
BigInteger big1 = BigInteger.valueOf(1L); // 의미 명확
BigInteger big2 = new BigInteger("1"); // 상대적으로 불분명
2. 호출 시마다 새 객체를 생성하지 않아도 된다 ♻️
- 캐싱, 싱글턴, 불변 객체 등에 유용
Boolean b = Boolean.valueOf(true); // 항상 동일 객체 반환 (true/false 재사용)
3. 하위 타입을 반환할 수 있다 🔁
- 반환 타입을 인터페이스로 숨기고, 실제 구현은 내부에서 결정 가능
public static List<Integer> emptyList() {
return Collections.emptyList(); // 실제로는 ImmutableEmptyList 반환
}
4. 제네릭 타입 추론이 가능 💡
Map<String, List<String>> map = Map.of(); // 타입 추론됨
5. 인스턴스화 로직을 한 곳에 모을 수 있다
of()
,from()
,valueOf()
등 여러 이름으로 상황별 분기 가능
6. 생성자를 감출 수 있다 🔒
- 외부에서
new
로 직접 생성 못하도록 제한
enum
,싱글턴
,도메인 객체 생성
등에서 유용
❌ 단점
단점 | 설명 |
생성자처럼 상속이 불가능 | User.of() 는 상속 구조에서 오버라이드 어려움 |
정적 메서드는 이름으로만 구분 | 오버로딩은 가능하지만 명확하지 않을 수 있음 |
개발자들이 of() 의 의미를 이해해야 함 | 생성자보다 문서화가 중요함 |
✅ 자주 쓰는 네이밍 관례
메서드 이름 | 용도 |
of() | 단순한 객체 생성 |
from() | 변환 |
valueOf() | 값 기반 생성 |
getInstance() | 싱글턴 or 캐시 |
newInstance() | 항상 새 객체 생성 |
instance() | 싱글턴 반환 (요즘 잘 안 씀) |
✅ 실제 예:
Enum.valueOf()
DayOfWeek day = DayOfWeek.valueOf("MONDAY");
✅ 요약
특징 | 설명 |
메서드 기반 객체 생성 | 생성자 대신 정적 메서드로 생성 |
이름 부여 가능 | 의도를 명확히 표현 |
캐싱, 싱글턴 등 유연 | new 보다 제어 쉬움 |
하위 타입 반환 가능 | 인터페이스와 함께 많이 사용 |
예제
User
package ex96.type2;
public class User {
private Integer id;
private String username;
private String password;
private String role; // Teacher, Student
// 학생
private Integer score;
private Integer level;
// 선생
private Integer sal;
private String position;
public static User createStudent() {
return new User(null, "ssar", "1234", "STUDENT", 100, 1, null, null);
}
public static User createTeacher() {
return new User(null, "ssar", "1234", "TEACHER", null, null, 3000, "MANAGER");
}
private User(Integer id, String username, String password, String role, Integer score, Integer level, Integer sal, String position) {
this.id = id;
this.username = username;
this.password = password;
this.role = role;
this.score = score;
this.level = level;
this.sal = sal;
this.position = position;
}
}
App
package ex96.type2;
public class App {
public static void main(String[] args) {
User teacher = User.createTeacher();
User student = User.createStudent();
}
}
💡 Of 와 From 의 차이
✅ 차이 요약
메서드 이름 | 사용 목적 | 예시 | 의미 |
of(...) | 직접 구성 요소로부터 객체 생성 | User.of(name, age) | “이 필드들로부터 만든다” |
from(...) | 다른 타입/객체로부터 변환 | User.from(UserDTO dto) | “변환해서 만든다” |
✅ of(...)
는 언제 쓰나?
- 필드 값 자체를 받아 객체를 만들 때
- 즉, 구성 요소들(primitive, enum, 값 객체 등)로 객체를 구성할 때
- 보통 여러 매개변수가 있든 없든 직접 필드 값을 넘길 때
예시
public class Point {
private final int x;
private final int y;
private Point(int x, int y) {
this.x = x;
this.y = y;
}
public static Point of(int x, int y) {
return new Point(x, y);
}
}
✔️ 특징
- 필드 하나만 있어도 쓸 수 있음 (매개변수 개수 상관 없음)
public static Boolean of(boolean value) {
return value ? TRUE : FALSE;
}
✅ from(...)
은 언제 쓰나?
- 다른 객체 또는 형식을 변환할 때
- 즉, 외부에서 들어온 DTO, JSON, API 응답 등에서 내부 도메인 객체로 바꿀 때
예시
public class User {
private String name;
private int age;
public static User from(UserDTO dto) {
return new User(dto.getName(), dto.getAge());
}
}
✔️ 특징
- 매개변수는 보통 1개 (변환 대상)
- 변환의 의미를 명확히 함
✅ 보너스: valueOf(...)
는?
- 기존 값이나 문자열로부터 객체를 만들 때
- 보통 기본 타입 → 객체로 바꿀 때 (Wrapper 클래스에서 자주 사용)
Integer i = Integer.valueOf("123");
DayOfWeek day = DayOfWeek.valueOf("MONDAY");
✅ 실전 구분 팁
상황 | 메서드 이름 추천 |
필드 값을 직접 넣어서 객체를 만들고 싶다 | of(...) |
DTO → Entity 변환하고 싶다 | from(dto) |
문자열, 숫자, enum 값 등으로 변환 | valueOf(...) |
✅ 요약 문장
of(...)
는 객체를 구성하는 값들로 객체를 만들 때
from(...)
은 다른 객체로부터 변환할 때
valueOf(...)
는 기본 값(String, int 등)으로부터 변환할 때
예제
Rectangle
package ex96.type2;
public class Rectangle {
private double x;
private double y;
private double width;
private Rectangle(double x, double y, double width) {
this.x = x;
this.y = y;
this.width = width;
}
public static Rectangle from(String value) {
if (value.equals("small")) {
return new Rectangle(50, 50, 50 * 50);
} else if (value.equals("medium")) {
return new Rectangle(100, 100, 100 * 100);
} else if (value.equals("large")) {
return new Rectangle(200, 200, 200 * 200);
} else {
throw new IllegalArgumentException("Invalid rectangle value");
}
}
public static Rectangle of(double x, double y) {
return new Rectangle(x, y, x * y);
}
}
App
package ex96.type2;
public class App {
public static void main(String[] args) {
Rectangle smallR = Rectangle.from("small");
Rectangle mediumR = Rectangle.from("medium");
Rectangle largeR = Rectangle.from("large");
}
}
Share article