개발 기록

[이펙티브 자바] 아이템 21,22 본문

JAVA

[이펙티브 자바] 아이템 21,22

수염차 2024. 11. 19. 18:30

## 21 인터페이스는 구현하는 쪽을 생각해 설계하라

 

### 기존 인터페이스에 디폴트 메서드 구현을 추가하는 것은 위험한 일이다.

1. 디폴트 메서드는 구현 클래스에 대해 아무것도 모른 채 합의 없이 무작정 '삽입' 될 뿐이다.

 

- 디폴트 메서드 : 인터페이스에 있는 구현 메서드 (메서드 앞에 default 예약어. 구현부 {} 가 있다.)

- - 기본 메서드는 이미 구현되어 있기 때문에 호출하여 사용할 수 있다.

public interface MyInterface {
    // 추상 메서드
    void abstractMethod();

    // 기본 메서드
    default void defaultMethod() {
        // 기본 구현 코드
        System.out.println("This is a default method.");
    }
}
public class MyClass implements MyInterface {
    @Override
    public void abstractMethod() {
        // 추상 메서드 구현 코드
        System.out.println("Abstract method implementation.");
    }
    
     public static void main(String[] args) {
        MyClass obj = new MyClass();
        obj.abstractMethod(); // 출력 : Abstract method implementation.
        obj.defaultMethod();  // 출력 : This is a default method.
    }
}

 

 

예시 ) Collection에 있는 removeIf 메서드

// Collection.java
default boolean removeIf(Predicate<? super E> filter) {
    Objects.requireNonNull(filter);
    boolean removed = false;
    final Iterator<E> each = iterator();
    while (each.hasNext()) {
        if (filter.test(each.next())) {
            each.remove();
            removed = true;
        }
    }
    return removed;
}
static class SynchronizedCollection<E> implements Collection<E>, Serializable {

    ...
    public int size() {
        synchronized (mutex) {return c.size();}
    }
    ...
}

-> removeIf는 동기화 처리가 되어있지 않지만 SyncronizedCollection 인스턴스에서 호출 가능.

-> 모르고 호출하면 동시성 관련 오류 발생할 수 있다. (ConcurrentModificationException)

 

public class HaveToOverride implements Collection {

    // 위험한 default 메서드를 재정의해서, 사용하지 못하도록 한다. 
    @Override
    public boolean removeIf(Predicate filter) {
        throw new UnsupportedOperationException();
    }
}

 

 

2. 디폴트 메서드는 기존 구현체에 런타임 오류를 일으킬 수 있다.

- 자바의 메서드 접근 규칙에 따른 오류 발생 : 자바는 인터페이스보다 클래스가, 상속한 클래스가 우선순위를 가진다. 

public class SuperClass {
    private void hello() {
        System.out.println("hello class");
    }
}

public interface MarkerInterface {

    default void hello() {
        System.out.println("hello interface");
    }
}
public class SubClass extends SuperClass implements MarkerInterface{
    public static void main(String[] args) {
        SubClass subClass = new SubClass();
        subClass.hello();
    }
}

위에서 SubClass는 SuperClass를 상속했기 때문에 인터페이스의 메서드보다 클래스의 메서드로 먼저 접근한다.

그런데 접근하고보니 접근한 메서드는 private이었기 때문에 위와 같은 에러가 발생한다. 

  • default 메서드가 추가되었고, 만약 구현체에서 사용하고 싶지 않다면 default 메서드를 오버라이딩 해서 사용하지 못하도록 한다. 

## 22 인터페이스는 타입을 정의하는 용도로만 사용하라

 

### 상수 인터페이스 안티패턴

public interface PhysicalConstants {

    static final double AVOGADROS_NUMBER = 6.022_140_857e23;
    static final double BOLTZMANN_CONSTANT = 1.380_648_52e-23;
    static final double ELECTRON_MASS = 9.109_383_56e-31;

}

1. 클래스 내부에서 사용하는 상수는 내부 구현에 해당. 상수 인터페이스는 내부 구현을 외부로 노출하는 행위.

2. 사용자에게 혼란을 줌

-  final이 아닌 클래스가 상수 인터페이스를 구현하게 되면 하위 클래스가 상수들로 오염됨.

 

### 상수를 정의하는 방법

1. 사용되는 클래스나 인터페이스 자체에 추가.

2. 인스턴스화 할 수 없는 유틸리티 클래스에 담기.

public final class PhysicalConstants {

    private PhysicalConstants() { } // 인스턴스화 방지

    public static final double AVOGADROS_NUMBER = 6.022_140_857e23;
    public static final double BOLTZMANN_CONSTANT = 1.380_648_52e-23;
    public static final double ELECTRON_MASS = 9.109_383_56e-31;

}
  • 인터페이스는 타입을 정의하는 용도로만 사용해야 한다. 상수 공개용 수단으로 사용하지 말자.

'JAVA' 카테고리의 다른 글

[이펙티브 자바] 아이템 26  (1) 2024.12.02
[이펙티브 자바] 아이템 24  (0) 2024.11.27
[이펙티브 자바] 아이템 18  (0) 2024.11.04
[이펙티브 자바] 아이템 15  (0) 2024.10.29
[이펙티브자바] 아이템12  (0) 2024.10.23
Comments