개발 서적/모던 자바 인 액션

[모던 자바 인 액션] Chapter13. 디폴트 메서드

더즈 2022. 4. 5. 16:03

디폴트 메서드

  • 전통적인 자바 인터페이스를 구현하는 클래스는 인터페이스의 모든 메서드 구현을 제공하거나 슈퍼 클래스의 구현을 상속받아야 한다.
  • 인터페이스를 바꾸고 싶을 때 문제 발생 -> 모든 구현체를 다 고쳐야 함
  • 자바 8 인터페이스의 변화
    • 인터페이스 내부에 정적 메서드 사용
    • 디폴트 메서드 기능 사용
    • 즉 메서드 구현을 포함한 인터페이스를 정의할 수 있다.
    • 기존 코드 구현을 바꾸지 않도록 하면서 인터페이스를 바꿀 수 있다.
  • 결국 추상 클래스?
  • 디폴트 메서드는 주로 라이브러리 설계자들이 사용한다.
  • 정적 메서드와 인터페이스
    • 보통 자바에서는 인터페이스와 인터페이스를 활용할 수 있는 정적 메서드를 정의하는 유틸리티 클래스를 활용한다. (Collection 인터페이스를 활용하는 Collections)
    • 자바 8 이후에는 인터페이스에 정적 메서드를 사용함으로써 유틸리티 클래스가 필요가 없어졌지만 이전 버전과의 호환성을 유지할 수 있도록 자바 API에는 유틸리티 클래스가 남아있다.

13.1 변화하는 API

  • 디폴트 메서드가 없을 때 인터페이스가 변경되면 발생되는 문제
    1. 라이브러리의 인터페이스에 새로운 메서드 추가
    2. 라이브러리 사용자가 라이브러리 인터페이스를 사용해 구현한 클래스는 바이너리 호환성 덕분에 새로운 메서드를 추가하지는 않아도 됨
    3. 하지만 언젠가 누군가가 인터페이스를 인수로 받아 새 메서드를 호출하고, 그 인터페이스 구현체가 새 메서드를 구현하지 않은 것이라면 런타임 에러가 발생한다.
      • Exception in thread “main” java.lang.AbstractMethodError
    4. 새 메서드를 구현하지 않은 구현체를 포함하는 전체 애플리케이션을 재빌드할 때도 컴파일 에러가 발생한다.
  • 공개 API를 고치면 기존 버전과의 호환성 문제가 발생한다. (때문에 자바 컬렉션 API 같은 기존 API는 고치기 어렵다)
  • 바이너리 호환성, 소스 호환성, 동적 호환성
    • 바이너리 호환성: 추가된 메서드를 호출하지만 않으면 새 메서드 구현 없이도 기존 클래스 파일 구현이 잘 동작함
    • 소스 호환성: 코드를 고쳐도 기존 프로그램을 성공적으로 재컴파일할 수 있음을 의미한다.
    • 동적 호환성: 코드를 바꾼 후에도 같은 입력 값이 주어지면 프로그램이 같은 동작을 실행한다는 의미다. (인터페이스에 메서드를 추가해도 호출할 일이 없는 경우)

13.2 디폴트 메서드란 무엇인가

  • 자신을 구현하는 클래스에서 구현하지 않아도 되는 새로운 메서드 시그니처를 제공한다.
  • 즉 디폴트 메서드를 추가하면 소스 호환성이 유지된다.
  • 추상 클래스와 자바 8의 인터페이스
    • 추상 클래스는 한 개만 상속 받을 수 있지만 인터페이스는 다중 상속이 가능하다.
    • 추상 클래스는 인스턴스 변수로 공통 상태를 가질 수 있다. 하지만 인터페이스는 인스턴스 변수를 가질 수 없다.

13.3 디폴트 메서드 활용 패턴

  • 디폴트 메서드를 이용하는 두 방식: 선택형 메서드다중 상속
  • 선택형 메서드
    • 인터페이스를 구현하는 클래스에서 잘 사용하지 않는 추상 메서드의 구현을 비워 놓는 것을 본 적 있을 것이다. (Iterator의 remove 메서드)
    • 디폴트 메서드를 이용하면 인터페이스에서 구현이 가능하므로 빈 구현을 제공할 필요가 없다. (불필요한 코드 줄임)
  • 동작 다중 상속
    • 인터페이스는 다중 상속이 가능하다.
    • ArrayList는 1개의 추상 클래스, 6개의 인터페이스를 상속한다.
    • 예: 어떤 모양은 회전할 수 있고 움직일 수 있지만 크기는 조절하지 못한다.
      • implements Rotatable, Movable (Resizable은 구현X)
  • 옳지 못한 상속
    • 1개의 메서드를 재사용하려고 100개의 메서드와 필드가 정의되어 있는 클래스를 상속받는 것은 좋은 생각이 아니다.
    • 이럴 때는 델리게이션, 즉 멤버 변수를 이용해서 클래스에서 필요한 메서드를 직접 호출하는 메서드를 작성하는 것이 좋다.
    • 인터페이스는 필요한 기능만 포함하도록 최소한으로 유지해야 한다. 재조립을 용이하게 하기 위해.

13.4 해석 규칙

  • 인터페이스 다중 구현 중에 같은 시그니처의 디폴트 메서드를 같은 경우가 생길 수 있다.
  • 실제로 자주 일어나지는 않지만 어떤 메서드가 실행될지 다음 세 규칙을 따른다.
    1. 클래스나 슈퍼 클래스에서 정의한 메서드가 디폴트 메서드보다 우선권을 갖는다.
    2. 클래스 다음으론 서브 인터페이스가 이긴다. 즉 인터페이스 B가 인터페이스 A를 상속한다면 B가 A를 이긴다.
    3. 위 두 규칙으로도 정해지지 않았다면 여러 인터페이스를 상속받는 클래스가 명시적으로 디폴트 메서드를 오버라이드하고 호출해야 한다.