Effective Java Reloaded

Effective Java Reloaded는 구독하고 있는 서민구 님의 블로그 글 ‘Effective Java Reloaded‘에서 발견한 것인데, Effective Java 의 저자인  Joshua Bloch 가 JavaOne 2006 에서 발표한 내용이다.

이 material의 도입부에서 Joshua는 “Effective Java Hasn’t Yet Been Reloaded, but I Have Plenty of Ammunition” 이라는 얘기를 하고 있다. 조만간 Effective Java  다음 판이 나오리라고 기대할 수 있을 듯 하다. 몇가지 인상적인 내용을 적어보면,

Builder Pattern with Builder’s setter methods that return Builder

NutritionFacts twoLiterDietCoke = new NutritionFacts.Builder("Diet Coke").sodium(1).build();

 위와 같은  Builder Pattern이 새로운 것은 아니지만, Joshua Bloch가 얘기하고 있는 것은, 이러한 패턴을 많은 수의 optional parameter를 가진  constructor의 대안이라는 것이다. 내가 직접 디자인하는 경우에는 constructor에 parameter가 많아야하는 경우를 가능한 한 피하려고 하기 때문에, 이 정도의 대안이 필요할 정도의 클래스를 디자인하게 되는 건 드문 일이지만, 간혹 수많은 parameter들을 가진  constructor들의 overloading으로 점철된 클래스를 맞닥뜨리게 되기도 한다. 이 경우 난 setter method와 Factory Pattern을 조합하는 형태(Dependency Injection의 Setter Injection)로 리팩토링 방법을 권장하는데, 아무래도 invariants를 벗어나는 instance를 생산한다는 것은 개운하지는 않은 일이다. 이런 경우에 위와 같은 Builder Pattern이 약간이나마 더 우아한 해결책이 될 수 있다.

한편, 이렇게, 간단한 개념을 표현하기 위해서 복잡한 방법을 동원해야한다는 것은 자주 프로그래밍 언어의 한계를 느끼게 한다. 언제쯤 Factory Pattern이나  Builder Pattern을 프로그래밍 언어 자체에서 지원할런지.

Erasure/Wildcard 때문에 복잡한 Generic

Generic이 명시하는 타입은 컴파일 단계에서만 유효할 뿐, 실행 단계에서는 타입 정보는 모두 사라지고 Erasure 만이 남는다. 아마 기존의  VM을 어느 정도 유지하면서도 Generic의 이점을 얻기 위해서 이러한 방법을 선택했으리라고 생각된다. 하지만, 동시에 Generic을 복잡하게 만드는 원인이 되기도 한다. (Joshua Bloch도 아마도 미래의 Java에서는 없어질 것이라고 언급하고 있다.)

Wildcard의 존재도 Generic을 복잡하게 만드는 개념 중의 하나인데 이를 가장 극명하게 드러나는 것이 바로 이 material의  Pop Quiz다.

public static void rotate(List<?> list) {
    list.add(list.remove(0));
}

언뜻 보기에 정상적으로 보이는 이 코드는  컴파일이 되지 않는 코드다. 자세히 들여다보면, list.add() 메서드와 list.remove()  메서드에서의 list의 타입은  wildcard type이고, List<?>와 List<?>라고 볼 수 있다. 그렇다면, list.add(List<?>) 에는  List<?> 를 넣을 수 있을까?  List<?>는, Object 클래스 또는 Object 클래스의 subclass의 List임이 보장될 뿐이기 때문에, List<?>와 List<?>를 같은 타입인지는 비교할 수는 없다.

Joshua는 같은 material에서 type parameter 자체가 사용되는 일이 없다면, wildcard를 사용하라고 조언하고 있지만, 과연 그렇게 해서 얻는 것이 무엇인지 의문이 든다. parameterized type 대신 wildcard type을 써야만 하는 이유가 뭘까? wildcard 관련 JSR을 살펴봐야겠다.

Erasure와  Wildcard 를 이해하지 못하면 Java Generic의 반만 이해한 것에 불과하다.

private final is the norm

필요하지 않은 자유도를 가능한 한 제약해야한다는 원칙을 따르고 있다면, 필드(멤버 변수)들은 물론, 파라미터 변수, 로컬 변수까지 final을 붙여야 하는가 하는 고민을 한번쯤 해봤을 것이다. 이는 C++ 프로그래밍 언어의 const에 관해서도 비슷한 이슈가 발생하한다. 결론부터 얘기하자면, 여기서 우리는 타협이 필요하다. 모든 타입 선언에 final (또는 const)을 붙이는 것은 프로그래머에게 상당한 비용을 요구하는 일이지만, 그로 인해 얻는 것이 그 비용을 상회하리라는 보장은 없기 때문에 그렇다. 따라서, 우리가 final을 사용함으로써 무엇을 얻는가를 따져보아야 한다.

이를테면, 상대적으로 좁은 메서드 scope로 격리되어있어서 그 영향이 적은 파라미터 변수, 로컬 변수 값의 변경은 제한된 코드 블럭에서 일어나기 때문에, 상대적으로 변수를 제어하거나 변수로 인해 발생한 문제를 수정하기도 쉬운 편이다. (이것이 메서드를 짧게 쓰라는 이유 중의 하나다.) 하지만, 클래스의 필드들의 경우에는 파라미터 변수나 로컬 변수들과는 접근 범위가 상당히 넓은 편이다. (클래스의 모든 메서드들 뿐만 아니라, 접근 권한에 따라 외부의 불특정 다수가 접근할 수 있으므로). 따라서, 필드를 가능한 한 final로 사용하는 것은 얻을 수 있는 이익이 크다.

비용-효율적(cost-effective)한 지점에서 타협을 해야한다는 얘기는, static typing과  dynamic typing 논쟁(아마도 이 외에도 많은 프로그래밍 언어 논쟁)에도 적용시킬 수 있다. 변수의 파라미터 타입을 명시하지 않아도 되는 언어를 사용하다보면, 주의를 기울인다고 하더라도 실수할 가능성이 있다. 설령 이러한 가능성이 매우 낮더라도, 한번 실수를 하게되면, 드러나지 않은 문제를 찾아내기란 결코 쉽지 않다. (strong typing 특유의 편안함은 여기서 온다.) 그렇다고 해서  static typing만을 극단적으로 추구한다면, 불필요한 수준의 타입 명시로 인해 개발  비용은 높아지고, 충분히 유연하지 못한 언어상의 한계로 인해 디자인에서 어려움을 겪게 될 것이다. 결국 우리가  static/dynamic typing 사이에서 취해야할 행동은 마찬가지로 cost-effective 한 어떤 지점에서의 타협이 될 것이다. C# 3.0의 ‘Static Typing Where Possible, Dynamic Typing When Needed‘를 참고해보길 바란다.

댓글 달기

이메일 주소는 공개되지 않습니다.

이 사이트는 스팸을 줄이는 아키스밋을 사용합니다. 댓글이 어떻게 처리되는지 알아보십시오.