인터페이스와 클래스

  • 인터페이스와 클래스는 자바의 핵심 개념
  • 자바8에 람다와 함수형 프로그래밍이 추가되면서 인터페이스에 대한 변화가 발생
    • 인터페이스에 대한 변화는 자바7에서 시작되었으며 자바8에서 대대적으로 변화됨
  • 여기서는 다음과 같은 사항들에 대해 살펴봄
    • 인터페이스의 문제점
    • 인터페이스의 진화
    • default 메서드
    • 다중 상속

인터페이스 사용 시 문제점

  • 인터페이스의 대표적인 문제점은 한번 배포되면 수정하기 어렵다는 점
  • 이미 배포된 인터페이스에 새로운 기능(메서드)를 추가해서 배포한경우 다음과 같은 문제가 발생
    • 해당 인터페이스를 구현한 모든 메서드에서 컴파일 에러 발생
    • 구현한 클래스가 너무 많아 한번에 수정이 불가능
    • 내부 뿐만 아니라 외부에서 해당 인터페이스를 사용하는 경우가 존재
    • 클래스를 수정하지 않고 인터페이스를 컴파일 해서 배포하면 NoSuchMethod 에러 발생
  • 추가되는 기능만 존재하는 인터페이스를 배포할 수 있지만 변경이 발생할때 마다 인터페이스를 계속 늘려나갈 수는 없음
  • 자바의 컬렉션 프레임워크도 이러한 문제가 존재
    • 컬렉션 프레임워크에서 가장 많이 사용되는 자료구조는 List, Map
    • List의 상위 인터페이스는 Collection
    • Collection은 최상위 인터페이스
    • JDK11을 기준으로 Collection에는 1.2, 1.8, 9, 11에서 추가된 메서드가 존재
    • 그 사이 버전에서는 추가가 안된것인가?
      • 1.3,1.4, 5,6,7버전에서는 영향이 너무커 쉽게 추가할 수 없었음
    • 만약 추가되어 컴파일에러나 NoSuchMethod가 발생하는 상황이 발생하면 개발자들은 다음과 같은 두 가지 경우에서 고려
      • 전체 소스를 재컴파일하고 에러가나면 메서드 추가
      • JDK를 업데이트 하지 않음 (대부분 이 방법을 선택)
    • 이처럼 인터페이스에 변화를 주는것은 역효과를 발생시킬수 있음
    • 자바 아키텍트들은 Collection에 대한 유틸리티인 Collections를 추가하여 여러가지 기능을 제공
      • 자바7 까지는 collection에 sort가 없어 collections의 sort를 사용했지만 자바8 부터는 collection에서 제공하는 sort를 사용

인터페이스의 진화

인터페이스에 메서드를 추가하는것에 대한 문제점이 이렇게 심각하다면 자바8 부터는 어떻게 메서드를 추가할 수 있게 된것인지 알아봅니다.

  • 자바 아키텍트들은 이러한 문제를 해결하기 위해 자바 8에서 인터페이스 기능을 대대적으로 변경
  • 그 핵심은 디폴트(default) 메서드

자바 인터페이스의 발전과정

  • 자바 1.1
    • 상수 선언가능 - public static final로 인식
    • 추상 메서드만 선언가능
  • 자바 1.2
    • 중첩 클래스 선언가능
    • 중첩 인터페이스 선언가능
    • 둘다 public static이어야 하며 생략가능
      • 인터페이스 내부에 클래스는 내부클래스가 아닌 중첩(static 생략해도)클래스로 선언
  • 자바 5
    • 중첩 열거형 선언가능
    • 중첩 어노테이션 선언가능
    • 둘다 public static이어야 하며 생략할 수 있음
  • 자바 8
    • 실제 코드가 완성되어 있는 static 메서드 선언가능
    • 실제 코드가 완성되어 있는 default 메서드 선언가능
    • 둘다 public이며 생략가능
  • 자바 9
    • private 메서드 선언 가능

결론적으로 현재 인터페이스에는 총 9가지 항목을 선언가능

  • 상수
  • 추상 메서드
  • 중첩 클래스
  • 충첩 인터페이스
  • 중첩 열거형
  • 중첩 어노테이션
  • static 메서드
  • default 메서드
  • private 메서드

default, static, private 메서드

  • 인터페이스 내의 default, static, private메서드는 메서드 내용이 존재
  • default 메서드는 메서드를 직접 구현하겠다고 컴파일러에게 알려주는 역할을 함
  • static 메서드와 private메서드는 별도의 키워드 정의 없이 메서드의 명세를 선언하고 내용을 정의하면 됨
  • static, private은 기존 인터페이스에 허용되지 않던 형태라 컴파일러가 혼란을 일으키지 않지만 default는 default라고 명시적으로 선언을 해주는 형태를 가짐

클래스와의 차이점과 제약 조건

  • default메서드로 인해 추상클래스와 인터페이스의 차이점이 모호 해질수 있음
  • 추상클래스와 인터페이스는 다음과 같은 차이를 가짐
    • 추상클래스는 맴버 변수를 가질 수 있음, 인터페이스는 static 변수만 가질 수 있음
    • 클래스는 오직 하나의 클래스만 상속할 수 있지만 인터페이스는 여러개를 상속받거나 구현가능
  • default 메서드를 호출하고 싶을때는 다음과 같이 사용
    • CustomInterface 선언된 getName이라는 default 메서드를 재정의
    • 보통 상위 메서드에 접근하기 위해 super를 사용하지만 인터페이스의 default메서드는 다음과 같이 인터페이스 이름으로 호출
public class CustomClass implements CustomInterface {
  // ...
  @Override
  public String getName() {
    CustomInterface.super.getName();
  }
}

다중 상속 관계

  • 자바는 다중 상속을 지원하지 않음
  • private 메서드는 상속되지 않음
  • static 메서드는 인터페이스 또는 클래스 레벨로 정의되므로 메서드 오버라이드 범위에 속하지 않음
  • default 메서드를 가진 여러개의 인터페이스를 구현할 경우 다중 상속의 효과를 얻게됨
  • 이런 다중 상속으로 인해 다이야몬드 구조의 상속이 발생할 수 있음
  • 상속받는 두개의 인터페이스에 같은 이름의 default 메서드가 존재하면 컴파일에러가 발생
  • 이를 해결하기 위해 클래스에서 두개의 인터페이스에 동일한 이름으로 구현된 default 메서드와 같은이름의 메서드를 정의함(오버라이딩)으로써 해결가능
  • 각 인터페이스에 존재하는 default 메서드는 위의 예제 코드와 같이 호출
  • 만약 클래스를 상속받고 인터페이스를 구현하는데 동일한 이름의 메서드(인터페이스에는 default메서드)가 존재한다면 클래스의 메서드가 우선호출됨

인터페이스와 클래스 호출관계 3가지

  1. 클래스가 인터페이스에 대해 우선순위를 가짐
  2. 1번 관계를 제외하고 상속관계에 있을 경우 하위 클래스/인터페이스가 상위 클래스/인터페이스보다 우선순위를 가짐
  3. 위 두가지 경우를 제외하고 메서드 호출에 모호성이 존재할 경우 컴파일 에러가 발생할 수 있음

References