인스턴스 : 클래스(설계도)를 바탕으로 만들어진 구체적인 객체
public 생성자 : 클래스의 인스턴스를 생성할 때 초기화를 담당하는 특별한 메서드
public class Car {
private String color; // Car 클래스의 필드
// Car 클래스의 public 생성자
public Car(String color) {
this.color = color; // 생성자를 통해 Car 인스턴스의 색상을 초기화
}
// Car 클래스의 메서드
public void drive() {
System.out.println(color + " 차가 달립니다.");
}
}
// Main 클래스에서 Car 클래스의 인스턴스 생성 및 사용
public class Main {
public static void main(String[] args) {
Car myCar = new Car("빨간색"); // Car 클래스의 인스턴스를 생성
myCar.drive(); // "빨간색 차가 달립니다." 출력
}
}
위 예시에서 Car 클래스에는 색상(color)를 초기화하는 public 생성자가 있다. Main 클래스에서는 이 생성자를 사용해 Car 클래스의 인스턴스를 생성하고, drive 메서드를 호출한다. 이처럼 public 생성자를 통해 클래스 외부에서 인스턴스를 쉽게 생성하고 사용가능하다.
정적 팩토리 메서드는 클래스의 인스턴스를 반환하게하는 정적 메서드이다. 이 방법은 클래스의 인스턴스를 생성하고 반환하기 위한 'new' 키워드를 직접사용하지않고, 클래스 내부에 정의된 특정 메서드를 통해 객체의 인스턴스를 얻는 방식이다.
정적 팩토리 메서드의 예시 :
public class Boolean {
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
}
같은 코드를 public 생성자로 사용했을 때 :
public class Boolean {
private boolean value;
// Public 생성자
public Boolean(String s) {
this.value = Boolean.parseBoolean(s);
}
// valueOf 메서드 대신 사용하는 생성자
// Boolean myBool = new Boolean("true");
// 나머지 클래스 구현
}
정적 팩토리 메서드의 장점 5가지 :
1. 이름을 가질 수 있다.
2. 호출될 때 마다 인스턴스를 새로 생성하지는 않아도 된다.
3. 반환 타입의 하위 타입 객체를 반환할 수 있다.
4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.
5. 정적 팩토리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.
이 모든 장점을 하나의 코드안에 담아보았다 :
public class VehicleFactory {
private static final Map<String, Vehicle> cache = new HashMap<>();
// 1. 이름을 가질 수 있음: 메서드 이름을 통해 반환되는 객체의 의도를 명확히 할 수 있습니다.
public static Vehicle createElectricCar() {
return new ElectricCar();
}
// 2. 호출될 때마다 인스턴스를 새로 생성하지 않아도 됨: 인스턴스 캐싱을 통해 불필요한 객체 생성을 방지합니다.
public static Vehicle getBicycleInstance() {
// 캐시에서 인스턴스를 검색, 없으면 생성하여 캐시에 추가
return cache.computeIfAbsent("bicycle", k -> new Bicycle());
}
// 3. 반환 타입의 하위 타입 객체를 반환할 수 있음: 인터페이스 Vehicle의 하위 타입 반환
public static Vehicle createVehicle(String type) {
switch (type) {
case "electric":
return new ElectricCar();
case "bicycle":
return new Bicycle();
default:
throw new IllegalArgumentException("Unknown vehicle type");
}
}
// 4. 입력 매개변수에 따라 다른 클래스의 객체를 반환할 수 있음: createVehicle 메서드 참조
// (위에서 이미 설명함)
// 5. 정적 팩토리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 됨:
// 이 예시에서는 ElectricCar와 Bicycle 클래스가 이 메서드들을 사용하는 클라이언트 코드보다 나중에 작성될 수 있습니다.
private interface Vehicle {
}
private static class ElectricCar implements Vehicle {
}
private static class Bicycle implements Vehicle {
}
}
정적 팩토리 메서드의 단점도 존재한다.
1. 상속을 하려면 public 이나 protected 생성자가 필요하니 정적 팩토리 메서드만 제공하면 하위 클래스를 만들 수 없다.
2. 정적 팩토리 메서드는 프로그래머가 찾기 어렵다.
2번 단점을 보완할 수 있는 방법이있다. 바로 널리 알려진 규약을 따라 메서드 이름을 짓는것이다.
대표적인 명명방식들이 있다. 예를 들면 :
- valueOf - 주어진 매개변수를 가지고 해당 타입의 인스턴스를 반환합니다. 주로, 기본 데이터 타입의 래퍼 클래스에서 많이 볼 수 있습니다.
- 예: Integer.valueOf(int i)
- of - valueOf와 유사하지만, 더 간결한 버전으로 사용됩니다.
- 예: List.of("a", "b", "c"), EnumSet.of(ChronoField.DAY_OF_WEEK)
- getInstance - 인스턴스를 가져오되, 같은 인스턴스임을 보장하지 않습니다. 싱글턴 패턴이나 인스턴스 캐싱 시 사용될 수 있습니다.
- 예: Calendar.getInstance()
- getSingleton - getInstance와 유사하지만, 항상 같은 인스턴스를 반환함을 보장합니다. 주로 싱글턴 패턴 구현에 사용됩니다.
- 예: Runtime.getRuntime()
- newInstance - getInstance와 유사하지만, 매번 새로운 인스턴스를 생성해 반환합니다. 반복 사용 시 다른 인스턴스를 기대할 때 사용합니다.
- 예: Array.newInstance(Class<?> componentType, int length)
- getType - getInstance와 유사하지만, 반환되는 인스턴스의 타입이나 클래스에 더 집중할 때 사용됩니다.
- 예: Files.getFileStore(Path path)
- newType - newInstance와 유사하지만, 새로운 인스턴스를 생성할 때 사용되며, 생성되는 인스턴스의 타입을 더 명확히 하고자 할 때 사용됩니다.
- 예: Collections.newSetFromMap(Map<E, Boolean> map)
종합 : 정적 팩토리 메서드와 public 생성자는 각자 쓰임새가 있지만 정적 팩토리 메서드를 사용하는게 유리한 경우가 더 많으므로 정적팩토리 멕서드사용을 지향하자.
'이펙티브 자바' 카테고리의 다른 글
[이펙티브 자바] 아이템 6. 불필요한 객체 생성을 피하라 (0) | 2024.03.11 |
---|---|
[이펙티브 자바] 아이템 5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2024.03.11 |
[이펙티브 자바] 아이템 4. 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2024.03.11 |
[이펙티브 자바] 아이템 3. private 생성자나 열거 타입으로 싱글턴임을 보증하라 (0) | 2024.03.11 |
[이펙티브 자바] 아이템 2. 생성자에 매개변수가 많다면 빌더를 고려하라 (0) | 2024.03.11 |