인터페이스에 디폴트 메서드 기능이 생기면서 인터페이스를 구현하면 공통적인 기능을 사용할 수 있고 반드시 오버라이딩 하지 않아도 되는 메서드여서 추상 클래스보다 가볍게 사용할 수 있지만 그렇다고 모든 위험에서 해방된 것은 아니다.
- 기존 사용되고 있는 인터페이스에 디폴트 메서드 구현을 추가하는 것은 위험한 일이다.
- 디폴트 메서드는 구현 클래스에 대해 아무것도 모른 채 합의 없이 무작정 "삽입"될 뿐이다.
- 디폴트 메서드는 기존 구현체에 런타임 오류를 일으킬 수 있다.
- 인터페이스를 설계할 때는 세심한 주의를 기울여야 한다.
- 서로 다른 방식으로 최소한 세 가지는 구현해보자.
이미 다른곳에서 많이 사용되고 있는 인터페이스에 디폴트 메서드를 추가한다면 해당 클래스를 구현한 모든 클래스에게 해당 기능이 무조건 추가가 된다.
그로인한 얘기치 못한 문제들이 발생한다.
예로 Collection 인터페이스의 removeIf라는 디폴트 메서드인데 해당 기능은 아무런 문제가 없는 메서드로 인자로 전달된 조건으로 리스트의 아이템들을 삭제하는 편리한 기능이다.
하지만 이 인터페이스를 구현한 org.apache.commons.collections4.collection.SynchronizedCollection 입장에서는 위험한 기능이다. SynchronizedCollection 클래스의 모든 기능은 동기화를 통해 멀티스레드 환경에서 한 번에 오직 한 쓰레드만 SynchronizedCollection 의 메서드를 이용할 수 있는데 Collection의 removeIf 메서드는 어디에도 동기화 처리가 되어있지 않아 멀티 스레드 환경에서 안전하지 않은 컬렉션이 된다.
그래서 SynchronizedCollection 객체에서 누군가가 removeIf 메서드를 호출하면 ConcurrentModificationException 이 발생할 수 있다.
인터페이스를 구현한 하위 객체에서는 인터페이스에 새로운 디폴트 메서드가 생겼을 때 별도의 IDE에서 경고도 없어 알아차리기 어렵고 컴파일 에러가 나지 않기 때문에 새로운 디폴트 메서드 때문에 하위클래스에서 생각치 못한 동작 오류가 발생한다면 오버라이딩하여 사용해야 한다.
그나마 컴파일 오류가 나면 상용화 전에 발견되기 쉬워 수정할 수 있지만 그렇지 않은 런타임 오류가 발생하는 코드라면 서비스가 작동중에 발견될 소지가 많으므로 찾아내기 더욱 어렵다.
SubClass에서 SuperClass의 hello 메서드를 사용하려면 private 키워드로 오버라이딩 할 수 없는 메서드로 사용할 수 없다.
그런데 인터페이스에 hello 디폴트 메서드가 있다면? SubClass에서 오버라이딩 가능하지만 인터페이스에 있는 메서드보다 클래스에 있는 메서드가 더 우선순위를 갖기 때문에 런타임시 SuperClass에 hello() 메서드 접근할 수 없어서 예외가 난다.
ConcurrentModificationException
현재 바뀌면 안되는 것을 수정할 때 발생하는 예외
어떤 한 스레드가 컬렉션 순회 도중 다른 스레드가 해당 컬렉션을 변경하려고 할 때 순회한 결과가 예측이 불가할 수 있으므로 예외가 난다.
예를들어 1스레드가 컬렉션을 읽어들일 때 인덱싱 구조로 읽어 들이면서 100번째 요소를 삭제하려고 했을 때 다른 2스레드가 해당 컬렉션을 읽다가 5번째 요소를 삭제하면 1스레드가 순회를 마치고 나온 결과값은 100번만 삭제된 컬렉션이어야 하는데 5번까지 삭제된 컬렉션이 될 가능성이 있다.
Fail-Fast 개념이란 동작중 오류가 발생하면 바로 오류를 알리고 작업을 중단한다.
Fail-Safe 개념이란 동작 중 오류가 발생해도 작업을 중단하지 않고 진행한다.
Fail-Fast iterator 는 컬렉션 순회 중 변경되지 말아야할 값이 변경이 된다면 바로 ConcurrentModificationException을 던진다.
비단 멀티스레드 환경에서만 벌어지는 상황은 아니라 싱글스레드에서도 이런 상황이 발생할 수 있다.
Collection을 순회하는 중 3 요소를 삭제해야 하는데 순회 중 Collection 요소를 삭제하기 때문에 Collection Index가 변경되어 일부요소는 순회하지 않는 문제가 발생할 수 있어 예외를 던진다.
Collection이 아닌 Iterator를 사용하여 요소를 변경하는 경우 Exception을 던지지 않고 변경할 수있다. Iterator는 Index가 아닌 자기 자신의 요소에 다음 요소가 있다면 반복을 도는 구조로 안전하게 수정이 가능하다.
'개인룸 > 도윤' 카테고리의 다른 글
Item.23 태그 달린 클래스보다는 클래스 계층구조를 활용하라 (0) | 2023.03.20 |
---|---|
22. 인터페이스는 타입을 정의하는 용도로만 사용하라 (0) | 2023.03.13 |
Item 20. 추상 클래스보다 인터페이스를 우선하라. (0) | 2023.03.13 |
Item19. 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라 (0) | 2023.03.06 |
Item18. 상속보다는 컴포지션을 사용하라 (0) | 2023.03.06 |