태그

2014년 7월 6일 일요일

[번역] JDK 8 1/3 - Interface default method implementations

KSUG 페북에 아래 글에 대한 번역이 요청이 빈발(?) 하여 손들고 한번 해 봅니다.

본문 링크 : http://blog.hartveld.com/2013/03/jdk-8-13-interface-default-method.html

게시자 : 
게시일 : 2013/03/22
번역자 : 조인석(isi.cho@gmail.com)
번역일 : 2014/07/06

번역을 하다가 한글로 변경하기 애매 한 경우는 의역하거나 내버려 두었습니다. 참고하세요..
조언이나 교정은 언제든지 감사한 마음으로 받겠습니다. :)



==============================================================


Posted 
 by 

이 포스트는 JDK 8를 위한 짧은 시리즈 중에 첫 번째이다 - 나의 다음 포스트인 lambda expressions, 그리고 마지막 포스트인 Stream API도 참고하기 바란다.

현재, OpenJDK 개발팀은 JDK 8 기능들을 완성하기 위해 바쁜 나날을 보내고 있다. OpenJDK의 최근 Project lambda 빌드에서는 Java 8 프로그램을 작성할 수 있는 꽤 완벽한 자바 개발 환경을 포함하고 있다. 필자는 아직까지 이번 JDK에 대해 어떤 문제도 발견할수 없었고, 근래의 lambda-dev 메일링 리스트에도 버그 레포트 관련 내용은 찾아보기 힘들다. Java 8의 신규 기능들 중 필자가 설명할 람다 관련 기능 셋은 무척 흥미로운 주제다. 이번 포스트는 그 중에 하나인 "default methods(기본 함수)"를 다루고 있다. (역자 주 : default methods는 함수 접근 제어자가 default인 것을 의미하여 아래 글에서는 '기본 함수'로 표현하겠음)


Extending interfaces: default methods(확장 인터페이스: 기본 함수)

추상 메소드로 선언된 일반적인 인터페이스 함수는 해당 인터페이스를 구현한 클래스에서 반드시 정의(must be defined) 되어져야 한다. 이러한 '짐(burdens)'은 매 선언 함수를 구현해야 할 책임을 개발자에게 지게 한다. 더 중요한 것은 '배포(publication)' 이후에는 인터페이스의 확장이 불가하다는 것이다. 다르게 말하자면, 모든 개발자는 개발된 소스와 망가진 이전 소스, 컴파일된 바이너리 클래스와의 호환성을 반드시 고려해야 한다.

이러한 문제점들을 해결하기 위해서, JDK 8의 신규 기능 중 하나인 기본함수(default methods)는 이미 배포된 인터페이스의 확장을 가능케 한다. 기본 함수는 독립적으로 선언될 뿐만 아니라 인터페이스 내부에 정의도 가능하다.

예제를 한번 살펴 보자.:


public interface Person {

  public abstract String getFirstName();
  public abstract String getLastName();

  public default String getDisplayableFullName() {
    return getLastName() + ", " + getFirstName();
  }

  public abstract boolean isStudent();

  public default boolean isNotStudent() {
    return !isStudent();
  }

}

Person 인터페이스를 구현한 개발자는 추상함수인 getFirstName(), getLastName(), isStudent()를 반드시 구현해야 한다.  하지만 기본함수인 getDisplayableFullName() 혹은 isNotStudent() 는 구현하지 않아도 된다.  개발자는 마치 하위 클래스에 일반적인 non-final 클래스 함수를 오버라이딩 하는 것 처럼, 여전히 기본 함수를 오버라이딩 할 수 있다. 추상 클래스는 기본 함수를 추상 함수로 (재)정의 할 수 있으며, 하위 클래스로 하여금 재구현 할 수 있게 제한 할 수 있다. (간혹 '재추상화(re-abstraction)' 라고도 불림).


Use cases for default methods (기본 함수를 위한 유스케이스)


1) Extending existing interfaces (기존 인터페이스의 확장)
JDK 라이브러리 중 이 신규 기능을 위한 가장 중요한 유스케이스는 인터페이스에 대한 기존 구현체를 수정하지 않고도 확장이 가능하다는 점이다 : 이전에는 인터페이스에 신규 추상 메소드를 추가하면 모든 기존 구현체에 추가된 추상 메소드를 구현해야만 했었다. 그리고 기존 인터페이스를 확장하는 것은 앞으로 Collections API(나의 JDK 8에 대한 세 번째 포스트(third blog post) 참고)로 확장 될 예정인 신규 Stream API 의 이전 버전의 구현체와 호환이 되는지 확인 할 필요가 있었다.

신규 Collection 인터페이스에 대한 코드를 살펴보자:

public interface Collection<E> extends Iterable<E> {
  
  // Traditional, abstract methods:

  int size();
  boolean contains(Object o);
  boolean add(E e);
  boolean remove(Object o);
  // Etc...

  // New methods stream() and parallelStream() with default implementations:

  default Stream<E> stream() {
    return Streams.stream(() -> Streams.spliterator(iterator(), size(), Spliterator.SIZED), Spliterator.SIZED);
  }

  default Stream<E> parallelStream() {
    return stream().parallel();
  }

}

Collection 인터페이스는 stream() 와 parallelStream() 함수에 의해 확장된다. 두 함수 모두 반드시 기본 함수로 정의 되어져야 한다. 그렇지 않으면 기존 Collection을 구현체들 (가령, ArrayList나 3rd 파티 모듈들)은 깨지거나 다시 구현해야만 한다.

2) Additional design flexibility in the inheritance hierarchy (상속 계층에서의 추가 설계 유연성)

간혹, 서로 다른 구현 클래스간에 공유하는 기능이 있을 수 있다. 전통적으로는 이 클래스들을 위한 상위 (추상) 클래스를 작성하게 된다. 다른 방식으로는 이벤트 리스너를 정의하여 여러 함수를 가진 인터페이스가 구현 되어져야만 하지만, 대개 개발자는 단 하나의 이벤트(싱글 함수)에만 관심을 가지고 있다. 일반적으로는 추상 (어댑터) 클래스(abstract (adapter) class)를 확장함으로써, 이러한 인터페이스 함수를 위한 기본 구현체를 제공한다.
기본 함수 구현체는 위 두 사례에 대하여 더 나은 설계 유연성을 제공한다. 스칼라의 특성과 마찬가지로(Similar to traits in Scala), 기본 함수는 서로 다른 타입에 대하여 행위를 공유 혹은 재사용 할 수 있다. 그리고 클래스의 다중 인터페이스 구현이 가능해짐에 따라, 다중 상속의 안전성과 제약적인 형식(safe and limited form)이 제공된다. 상태를 공유하기 위해서는 (추상) 상위 클래스가 여전히 요구되어진다.


3) Alternative: extension methods (대안: 확장 함수)

C# 확장 함수(C# extension methods)의 다른 형태가 고려되어져 왔으며 이에 대한 대안이 된다. 그럼에도 불구하고, 확장 함수는  새로운 '정적-성(static-ness)'을 소개하고 있다 : 확장 함수는 첫 번째 인자값을 실행 대상 객체로 취하는 일종의 정적 함수이다.  또한 확장 함수는 기본적으로 코딩시 편리함을 제공한다 : IDE를 사용하는 프로그래머는 '.'를 통해 확장함수를 쉽게 호출 할 수 있다.
이러한 시도의 한 가지 문제점은 개발자가 함수를 오버라이딩 하지 않고, 전체 클래스 계층이 모든 케이스에 사용이 가능하게 구현된 소스로 인해 정체될 수 있다는 부분이다. 대개 이런식으로 큰 규모내의 추가된 정적 함수를 소개 하는 것은 바람직 하지 않다(considered undesirable). 또한, 확장 함수는 reflection을 사용하는 경우 제대로 동작하지 않는다 : 확장 함수는 클래스의 정의된 함수가 아니기 때문에 찾을 수가 없다 (예를 들자면, 메일 클래스가 로딩되고 난 다음 reflection을 통해서만 확장 함수를 포함한 클래스가 로드 되었다고 치자 - JVM은 확장 함수의 존재를 알 방법이 없다).

댓글 없음 :

댓글 쓰기