Java/이펙티브 자바
아이템21. 인터페이스는 구현하는 쪽에서 생각해 설계하라.
PHM
2023. 9. 5. 11:32
핵심 정리
- 기존 인터페이스에 디폴트 메서드 구현을 추가하는 것은 위험한 일이다.
▷ ex) collection의 removeIf - synchronized 관련한 코드가 없기에 멀티쓰레드 환경에서 안전 X
ㆍ디폴트 메서드는 구현 클래스에 대해 아무것도 모른 채 합의 없이 무작정 "삽입" 될 뿐이다.
ㆍ디폴트 메서드는 기존 구현체에 런타임 오류를 일으킬 수 있다.
public class SubClass extends SuperClass implements MarketInterface {
public static void main(String[] args) {
SubClass subClass = new SubClass();
subClass.hello(); // SuperClass의 hello는 private, MarketInterface의 hello는 디폴트 메서드
// 항상 class가 메서드를 이기게 되어 있다.
// 런타임 오류 발생!
}
}
- 인터페이스를 설계할 때는 세심한 주의를 기울여야 한다.
ㆍ서로 다른 방식으로 최소한 세 가지는 구현해보자.
완벽 공략 - ConcurrentModificationException
: 현재 바뀌면 안되는 것을 수정할 때 발생하는 예외
- 멀티 스레드가 아니라 싱글 스레드 상황에서도 발생할 수 있다. 가령, fail-fast 이터레이터를 사용해 콜렉션을 순회하는 중에 콜렉션을 변경하는 경우
public class FailFast {
public static void main(String[] args) {
// List<Integer> numbers = List.of(1,2,3,4,5); // of는 수정할 수 없는 컬렉션 - UnsupportedOperationException
List<Integer> numbers = new ArrayList<>(); // 변경가능한 컬렉션 - ConcurrentModificationException
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
// 이터레이터로 콜렉션을 순회하는 중에 Collection의 remove를 사용한다면...exception 발생
for (Integer number : numbers){
if (number == 3) {
numbers.remove(number);
}
}
// 1. 이터레이터의 remove 사용하기 - 안전
for (Iterator<Integer> iterator = numbers.iterator(); iterator.hasNext();) {
Integer integer = iterator.next();
if(integer == 3) {
iterator.remove();
}
}
// 2. 인덱스 사용하기 - 안전
for (int i = 0; i < numbers.size() ; i++) {
if (numbers.get(i) == 3) {
numbers.remove(numbers.get(i));
}
}
// 3. removeIf 사용하기
numbers.removeIf(number -> number == 3);
// 출력
numbers.forEach(System.out::println);
}
}