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);
        
        
        
    }
}