자바 개발자라면 한번쯤은 읽어봐야하는 책이라고 하는 이펙티브 자바입니다.
책 구매는 8월 쯔음에 한거같은데, 프로젝트 1개가 어느 정도 완성된 거 같아 슬슬 하나씩 읽어보면서 코드 좀 갈아보려고요.
이거 외에도 토비의 스프링이나 종만북, 노랭이도 일단 있긴한데 그닥 손이 안가는 거 보면 당장 필요한 시기는 아닌가봅니다.
TIP미리 말하지만 부정안하고 제가 책 읽는 것을 진짜 싫어합니다.
- 제가 알고리즘 공부하려고 종만북 샀던 것도 1개 읽고 1개는 그대로 방치된 상태입니다.
- 너무 방치하기엔 아까워서 개인 프로젝트 완수한 시점에 읽으면 좋아보이더라고요
정적 팩토리 메서드 (Static Factory Method)
-
객체의 생성을 담당하는 클래스 메서드입니다.
-
클래스의 인스턴스를 얻는 전통적 수단 public 생성자
-
또 알아야 할 게 있는데 그게 정적 팩터리 메서드
장점
- 이름을 가질 수 있음
- 호출될 때마다 인스턴스 새로 생성할 필요 없음
- 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있음
- 입력 매개변수에 따라 매번 다른 클래스 객체 반환할 수 있음
- 정적 팩터리 메서드를 작성하는 시점에 반환 객체의 클래스가 존재하지 않아도 됨
단점
- 상속하려면 public이나 protected 생성자가 필요해 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없음
- 정적 팩터리 메서드는 프로그래머가 찾기 어려움
사실 뭔 소린지 모르겠습니다
책 잘 읽고 똑똑하면 서울대갔지.. 제 머리는 암기하는 머리가 아니기에 우선 키워드만 땄습니다
머리가 좋지 않으니 하나하나 파서 해보죠
public 생성자
public은 알겠는데 왜 public 생성자인지부터 생각해야합니다.
public class Person { private String name;
public Person() { }
private Person(String name) { this.name = name; }}- 일단
public 생성자는 클래스 바깥에서 객체를 만들 수 있는 공개되어 있는 생성자를 말합니다.
Person p = new Person();우리는 내부에 Person이라는 것이 있기에 생성자를 이해했습니다.
그렇다면 public 생성자는?
- 앞에 public이 붙으면 public 생성자가 됩니다.
- 반대로 private이 붙으면 private 생성자가 되겠지요
- 이건 싱글톤에 사용하겠네요
정적 팩토리 메서드
그러면 또 익혀야 한다는 그 정적 팩토리 메서드는 뭘까요?
public class Person { private String name;
private Person(String name) { this.name = name; }
public static Person from(String name) { return new Person(name); }}음.. 분명 저런 코드를 과거에 짜던 기억은 있지만 어째서 좋은건가요?
장점 분석
NOTE책의 긴 글 잘 안읽히니까
핵심 키워드만 뽑아서 하나씩 해보겠습니다
이름을 가질 수 있음
그러네요. 이름 가질 수 있네요
// public 메서드new Person("SY");
// 정적 팩토리 메서드Person.from("SY");- 위 / 아래 쓰기엔 위가 편하겠지만, 의도 파악은 아래가 더 좋아 보입니다.
- 예로
Letter.to("SY")이나Letter.from("YJ")이런 느낌으로다가 의도에 맞게 만들 수 있겠네요.
호출될 때마다 인스턴스 새로 생성할 필요 없음
Person p1 = new Person("SY");Person p2 = new Person("SY");원래라면 제가 이런식으로 계속 만들텐데
public class Person { private static final Map<String, Person> CACHE = new HashMap<>();
private String name;
private Person(String name) { this.name = name; }
public static Person of(String name) { if (CACHE.containsKey(name)) { return CACHE.get(name); }
Person p = new Person(name); CACHE.put(name, p); return p; }}이러면 제 이름이 수 백명이라도 이미 있는 객체 그대로 쓰겠죠
즉, 매번 new 할 필요 없으니까 이것도 이해했습니다.
반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있음
음 인터페이스 뒤에 진짜 구현체를 숨긴다 그런 소리 같은데요?
public class GradeCalculator { public static List<Integer> getScores() { return new ArrayList<>(); }}예로 반환타입은 List인데 실제로는 하위 구현체인 ArrayList로 반환하는 것처럼요
즉, 정적 팩토리 메서드에 적용하면, 굳이 API 사용하는 사람한테 클래스 알려줄 필요가 없다. 그렇 소리 같습니다.
public interface Mobile { void call();}
class Galaxy implements Mobile {}class IPhone implements Mobile {}
public class MobileFactory { public static Mobile getPhone(String Type) { if ("Apple".equals(type)) { return new IPhone(); } return new Galaxy(); }}음, 그러니까 Mobile만 알면되는데 그게 뭔 폰인지는 알 필요 없다는 그런 거네요. 구체 클래스 이름은 가리고, 알 필요도 없어지면 확실히 좋긴 하네요.
입력 매개변수에 따라 매번 다른 클래스 객체 반환할 수 있음
음.. 이건 아마 유연하게 할 수 있다는 그런걸까요?
잘 모르는데 대충 EnumSet이 대표라곤 하는데 코드 뜯으면서 확인해보겠습니다.
EnumSet<Color> colors = EnumSet.of(Color.RED, Color.BLUE);일단 눈으로 보이는 건 EnumSet인데, 따로 public 생성자가 없네요? 정적 팩토리 메서드라는건 일단 바로 알긴했는데, 그래서 왜죠?
public abstract class EnumSet<E extends Enum<E>> {
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) { Enum<?>[] universe = getUniverse(elementType);
if (universe.length <= 64) { return new RegularEnumSet<>(elementType, universe); }
return new JumboEnumSet<>(elementType, universe); }}코드 중 일부 가져왔는데, 보시면 굉장히 재밌는 구조네요.
Enum 개수가 여러개든 아니든 일단 받고, 64개 이하면 RegularEnumSet을 리턴하고 그 이상은 JumboEnumSet을 리턴합니다.
또 들어가서 보면 RegularEnumSet은 long 변수 하나로 뭐 비트마스킹 써서 관리하고 있고, JumboEnumSet은 long[] 배열 써서 관리하던데 그냥 알아서 최적화해준다는 게 엄청나네요.
비트마스킹은 알고리즘 영역이고 쉽게 설명하면 이진수 표현으로 계산해서 효율 극대화한건데, 이건 웹 개발자가 현대에서 쓸 일이 거의 없기에 필요한 거 아니면 의식하지 않는 게 코딩을 재밌게 하려면 필요한 게 아니면 의식하지 않아도 될 거 같습니다.
정적 팩터리 메서드를 작성하는 시점에 반환 객체의 클래스가 존재하지 않아도 됨
마지막은 그냥 JDBC를 알려주는 거 같네요.
Connection conn = DriverManager.getConnection("jdbc:mysql://...");
우리가 MySQL을 쓸지 PostgreSQL을 쓸지 코드 짤 때는 모를 수 있잖아요?
이건 드라이버 나중에 등록하면 알아서 Connection 객체를 만들어 준다는 거 같습니다.
단점 분석
단점도 뜯어 봐야죠. 장점만 알아서 뭐합니까
상속하려면 public이나 protected 생성자가 필요해 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없음
public class Parent { private Parent() {}
public static Parent getInstance() { return new Parent(); }}
public class Child extends Parent { public Child() { super(); // Error }}이건 생성자를 막아버리니 다른 클래스가 상속받을 수 없다는 거네요.
- 뭐,, 요즘에는 안꼬이려고 다 때네려고 하는데 오히려 장점인게 아닌가 싶은데요..?
정적 팩터리 메서드는 프로그래머가 찾기 어려움
생성자처럼 주석 달던지 문서화하던지 그럴 수 없으니 그런 거 같습니다.
일종의 규칙이 있다는 데 그거만 대충 기억하면 나도 정적 팩토리 메서드를 할 줄 안다. 가 될 거 같습니다.
Rule
- from : 매개변수 하나 받아서 객체 생성할 때
Data a = Data.from(instant);
- of : 매개변수 여러 개 받아서 객체 생성할 때
Set<Rank> cards = EnumSet.of(JACK, QUEEN, KING);
- valueOf :
from과of의 더 자세한 버전String s = String.valueOf(true);
- instance, getInstance : 인스턴스를 반환하지만, 매개변수와 같은 값인지는 보장 안함
Calendar cal = Calendar.getInstance();
- create, newInstance :
getInstance와 같지만, 매번 새로운 객체 생성 보장Object array = Array.newInstance(classObject, length);
정리
- 결론은 내 프로젝트 안봐도
너 public 생성자 써서 한거 다 알고있음이라고 묻는 거 같네요. - 머리에 넣을 게 아니라 빨리 니 코드 고치고 다음꺼 봐라라는 거 같습니다.
- 첫장부터 이거 박아넣어서 제 코드를 뜯게 만드는 이 사람은 진짜 뭐하는 사람일까요?
- 이거 재밌네요. Item 90개 검토하려면 최소 3개월 박겠는데 리펙토링, 성능 개선 생각하면 당분간은 자바만 생각해도 될 거 같습니다