Notice
Recent Posts
Recent Comments
Link
«   2026/01   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

SSinsa

Chapter 09. 디폴트 메소드 본문

Java/Java 8

Chapter 09. 디폴트 메소드

SSinsa 2019. 10. 16. 09:20

API가 버전업이 되었을 때,

setRelativeSize 메서드가 추가되고 사용되면, 어플리케이션 구동시 컴파일에러가 난다.

 

인터페이스에 새로운 메서드를 추가하면 바이너리 호환성은 유지됨.

#바이너리 호환성 : 새로 추가된 메서드를 호출하지만 않으면 새로운 메서드 구현이 없이도 기존 클래스 파일 구현이 잘 동작한다는 의미

그러나 누군가 Resizable을 인수로 받는 Utils.paint에서 setRelativeSize를 사용하도록

코드를 바꿀 수 있다.

이때 setRelativeSize 메서드를 정의하지 않으므로 런타임 에러 발생

 

#문제점 정리

- 라이브러리를 관리하기가 복잠

- 사용자는 같은 코드에 예전버전과 새로운 버전 두 가지 라이브러리를 모두 사용해야함

---> 해결책 : 디폴트 메서드

 

> cf.

  • 바이너리 호환성 : 인터페이스에 메서드를 추가했을 때 추가된 메서드를 호출하지 않는 한 문제가 일어나지 않는 것
  • 소스 호환성 : 코드를 고쳐도 기존 프로그램을 성공적으로 재컴파일할 수 있음을 의미 (인터페이스에 메서드 추가는 소스 호환성 아님, 메서드 구현해야하므로)
  • 동작 호환성 : 코드를 바꾼 다음에도 같은 입력값이 주어지면 프로그램이 같은 동작을 실행한다는 의미

1. 디폴트 메서드

# 디폴트 메서드

- 호환성을 유지하면서 API를 바꿀수 있도록 하는 기능

- 인터페이스가 자신을 구현하는 클래스에서 메서드를 구현하지 않을 수 있는 새로운 메서드 시그니처를 제공

- 인터페이스를 구현하는 클래스에서 구현하지 않은 메서드는 인터페이스 자체에서 기본으로 제공

=> 디폴트메서드를 사용하면 인터페이스 상속시 소스 호환성이 유지된다

 

# 의문

- 인터페이스가 구현을 가질 수 있고 클래스는 여러 인터페이스를 동시에 구현할 수 있으므로 결국 자바도 다중 상속 지원?

- 인터페이스를 구현하는 클래스가 디폴트 메서드와 같은 메서드 시그너처를 정의하거나 아니면 디폴트 메서드를 오버라이드한다면?

=> 9.4절 해결가능한 규칙존재

 

# Predicate, Function, Comparator 등

-> Predicate.and 또는 Function.andThen 같은 다양한 디폴트 메서드 포함

(함수형 인터페이스는 오직 하나의 추상 메서드를 포함한다. 디폴트 메서드는 추상 메서드에 해당하지 않는다)

(즉 함수형 인터페이스에 추상메서드 1개, 디폴트 메서드 여러개가 가능)

 

 

cf. 추상클래스와 자바 8의  인터페이스

-> 추상클래스와 인터페이스 차이 (공통 : 둘 다 추상 메서드와 바디를 포함하는 메서드를 정의)

1. 클래스는 하나의 추상 클래스만 상속받을수 있지만 인터페이스를 여러 개 구현할 수 있다

2. 추상 클래스는 인터페이스 변수로 공통 상태를 가질 수 있다. 하지만 인터페이스는 인스턴스 변수를 가질 수 없다.

(인터페이스는 final, abstract 변수만 가능)

 

 

2. 디폴트 메서드 활용 패턴

->두가지 방식 존재 : 선택형 메서드 / 동작 다중 상속

 

 

1) 선택형 메서드

예를 들어 자바 8 이전에 인터페이스 안에 비어있는 메서드가 있었음.

-> ex) Iterator 인터페이스에 hasNext, next, 등이 있지만 remove는 빈 메소드

 

빈 메서드를 디폴트 메서드를 이용해 구현가능

interface Iterator<T> {
	boolean hasNext();
    T next();
    default void remove(){
    	throw new UnsupportedOperationException();
    }
}

 

2) 동작 다중 상속

클래스는 다중 상속을 이용해서 기존 코드를 재사용할 수 있음

-> 자바에서 클래스는 한 개의 다른 클래스만 상속 가능 but 인터페이스는 여러 개 구현할 수 있음

-> 이를 이용한 다중 상속

 

즉, 인터페이스에서 동작을 상속받음으로써 다중 동작 상속이 된다 (또는 다중동작상속)

 

 

# 기능이 중복되지 않는 최소의 인터페이스

기존에 있던 setRotationAngle과 getRotationAngle을 합쳐서 rotateBy라는 디폴트 메서드 만듬

(템플릿 디자인 패턴과 비슷함)

 

이런식으로 Moveable과 Resizeable도 각각 디폴트 메서드 정의함

이제 세가지 인터페이스를 다 상속하는 클래스 만들기

 

public class Monster implements Rotatable, Moveable, Resizable {
	...
}

Monster 클래스는 세 가지 인터페이스를 상속받으면서 동시에 각각에 디폴트 메서드도 구현하지 않아도 사용가능해짐

또는 이중 두 가지만 받는 클래스도 만들 수 있음

 

 

cf. 옳지 못한 상속

- 1개 메서드를 사용하려고 100개 메서드를 가진 클래스를 상속받는건 안좋음

-> 이럴땐 델리게이션 (delegation) : 멤버 변수를 이용해서 클래스에서 필요한 메서드를 직접 호출하는 메서드를 작성하는 것 (따라서 final로 선언된 클래스들 처럼 다른 클래스가 이 클래스를 상속받지 못하게 함, 원래 동작 바뀌지 않음)

ex) String 클래스

==> 따라서, 필요한 기능만 포함하도록 인터페이스를 최소한으로 유지하기!!

 

 

3. 디폴트메스드 해석

-> 같은 명에 디폴트 메서드를 가진 두 인터페이스를 상속받았을 때? 과연? (ex. 다이아몬드 문제)

1. 알아야 할 세 가지 해결 규칙 (위에서부터 우선 체크)

  • 클래스가 항상 이김! 클래스나 슈퍼클래스에서 정의한 메서드가 (우위) > 디폴트메서드
  • 위의 규칙 이외의 상황에서 서브인터페이스가 이김! 즉 B가 A를 상속 받는다면 B가 A를 이김
  • 여러 인터페이스를 상속받는 클래스가 명시적으로 디폴트메서드를 오버라이드하고 호출해야함

1) 디폴트 메서드를 제공하는 서브 인터페이스가 이긴다

                                               Q. 둘중 어떤 hello를 사용할까? A. B의 hello를 선택

해설

2번 규칙에따라 서브 인터페이스가 이기기 때문에

                                             Q. 둘중 어떤 hello를 사용할까? A. B의 hello를 선택

해설

1번규칙 : D는 hello를 오버라이드 하지 않았고 단순 인터페이스 A구현

             D는 A의 디폴트 메서드 구현을 상속받음

2번규칙 : 클래스나 슈퍼클래스에 메서드 정의가 없을 때는 디폴트 메서드를 정의하는 서브 인터페이스가 선택됨

             컴파일러는 A의 hello나 B의 hello 둘 중 하나를 선택해야함

             B가 A를 상속받는 관계이므로 Hello from B가 출력됨

 

                                            Q. 둘중 어떤 hello를 사용할까? A.  D의 hello를 선택

해설

1번 규칙에 따라 슈퍼클래스의 메서드 정의가 우선권을 갖기 때문

 

                                        Q. 둘중 어떤 hello를 사용할까? A.  C의 hello를 선택

해설

A에서 디폴트 메서드를 제공함에도 불구하고 C는 hello를 구현해야함

 

2) 충돌 그리고 명시적인 문제 해결

  

                                       Q. 둘중 어떤 hello를 사용할까? A. 에러가 발생

 

해설

1번, 2번 규칙으로는 적용 안됨, 상속관계가 없으므로 

따라서 자바 컴파일러는 어떤 메서드를 호출해야할지 알 수  없음

Error: class C inherits unrelated defaults for hello() form types B and A

 

 

 

이럴 때 개발자가 명시적으로 선택해야한다

따라서 위의 C 클래스는

public class C implements B, A {
	void hello() {
    	B.super.hello(); //명시적으로 인페B의 메서드 선택
    }
}    

 

 

3) 다이아몬드 문제

위 코드의 UML 다이어그램

 

C와 B는 A의 디폴트 메소드를 받지만

이러면 D는 C와 B중에 어떤 것을 받을지 모른다

따라서 컴파일 에러가 난다. 명시적으로 하나를 선택해야 호출가능

 

C에 추상메서드 hello를 추가하면

C는 A의 디폴트 메서드와 자신의 hello 추상메서드 중 자신의 것이 우선권을 가짐

따라서 D는 C와 B중 어떤것을 선택할지 몰라 또 에러

 

 

'Java > Java 8' 카테고리의 다른 글

8, 9장 통합 질문들  (0) 2019.10.16