→ 인스턴스를 생성하는 두가지 방법의 상대적인 장단점을 이해하고 사용하자. 정적 팩터리를 사용하는게 유리한 경우가 더 많기에 무작정 public 생성자를 제공하지 말자.
→ 선택적 매개변수가 많을 때 적절히 대응하기 어렵다.
public class NutritionFacts {
private final int servingSize; // (ml, 1회 제공량) 필수
private final int servings; // (회, 총 n회 제공량) 필수
private final int calories; // (1회 제공량당) 선택
private final int fat; // (g/1 회 제공량) 선택
private final int sodium; // (mg/1 회 제공량) 선택
private final int carbohydrate; // (g/1 회 제공량) 선택
public NutritionFacts(int servingSize, int servings) {
this(servingSize, servings, 0);
}
public NutritionFacts(int servingSize, int servings, int calories) {
this(servingSize, servings, calories, 0);
}
public NutritionFacts(int servingSize, int servings,int calories, int fat) {
this(servingSize, servings, calories, fat, 0);
}
public NutritionFacts(int servingSize; int servings, int calories, int fat, int sodium) {
this(servingSize, servings, calories, fat, sodium, 0);
}
public NutritionFactsdnt servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
this.carbohydrate = carbohydrate;
}
}
public class NutritionFacts {
//매개변수들은 (기본값이 있다면) 기본값으로 초기화된다
private int servingSize = -1; // 필수; 기본값 없음
private int servings = -1; //필수; 기본값 없음
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public NutritionFacts() { }
// 세터 메서드들
public void setServingSize(int val) { servingSize = val; }
public void setServings(int val) { servings = val; }
public void setCalories (int val) { calories = val; }
public void setFat(int val) { fat = val; }
public void setSodium(int val) { sodium = val; }
public void setCarbohydrate(int val) { carbohydrate = val; }
}
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public static class Builder {
// 필수 매개변수
private final int servingSize;
private final int servings;
// 선택 매개변수 - 기본값으로 초기화한다
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) {
calories = val; return this;
}
public Builder fat(int val) {
fat = val; return this;
}
public Builder sodium(int val) {
sodium = val; return this;
}
public Builder carbohydrate(int val) {
carbohydrate = val; return this;
}
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
→ 생성자나 정적 팩터리가 처리해야 할 매개변수가 많다면 빌더 패턴을 선택하는 게 더 낫다. 매개변수 중 다수가 필수가 아니거나 같은 타입이면 더 그렇다. 빌드는 점층적 생성자보다 클라이언트 코드를 읽고 쓰기가 훨씬 간결하고, 자바빈즈보다 훨씬 안전하다.
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public void leaveTheBuilding() { ... }
}
public class Elvis {
private static final Elvis INSTANCE = new ElvisO;
private Elvis() { ... }
public static Elvis getlnstance() { return INSTANCE; }
public void leaveTheBuildingO { ... }
}
위 두 방식으로 만든 싱글턴 클래스를 직렬화하려면 인스턴스 필드를 일시적이라고 선언하고 readResolve 메서드를 제공해야한다. 이렇게 하지 않으면 역직렬화할 때마다 새로운 인스턴스가 만들어진다.
// 싱글턴임울 보장해주는 readResolve 메서드
private Object readResolve() {
// '진짜‘ Elvis를 반환하고,가짜 Elvis는 가비지 컬렉터에 맡긴다.
return INSTANCE;
}
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}
→ 더 간결하고 추가 노력없이 직렬화 할 수 있다.
→ 아주 복잡한 직렬화 상황이나 리플렉션 공격에서도 제 2의 인스턴스가 생기는 일을 완벽히 막아준다.
→ 대부분 상황에서는 원소가 하나뿐인 열거 타입이 싱글턴을 만드는 가장 좋은 방법이다. 단, 만들려는 싱글턴이 Enum 외의 클래스를 상속해야 한다면 이 방법은 사용할 수 없다.
public class Utilityclass {
// 기본 생성자가 만들어지는 것을 막는다(인스턴스화 방지용)
private UtilityClass() {
throw new AssertionError();
}
... // 나머지 코드는 생략
}
이펙티브 자바를 친절하게 정리해주셔서 감사함니다..