DEV ℧ Developer Diary

[EffectiveJava] item22 - 인터페이스는 타입을 정의하는 용도로만 사용하라

인터페이스는 자신이 구현한 클래스의 인스턴스를 참조할 수 있는 타입 역할을 한다. 달리말해, 클래스가 어떤 인터페이스를 구현한다는것은 자신의 인스턴스로 무엇을 할 수 있는지를 클라이언트에 얘기해주는것이다.

인터페이스는 오직 이용도로만 사용해야한다.

지침에 맞지 않은 인터페이스

상수 인터페이스라는 것이 있다. 상수 인터페이스는 메서드없이, static final 필드로만 가득찬 인터페이스인데 이 상수들을 사용하려는 클래스에서는 정규화된 이름(Qualified name)을 쓰는걸 피하고자 인터페이스를 구현하곤 한다.

다음은 상수 인터페이스의 예시이다.

public interface PhysicalConstants {
    // 아보가드로 수 (1/몰)
    static final double AVOGADROS_NUMBER = 6.022_140_857e23;

    // 볼츠만 상수 (J/K)
    static final double BOLTZMANN_CONSTANT = 1.380_648_52e-23;

    // 전자 질량 (kg)
    static final final double ELECTRON_MASS = 9.109_383_56e-31;
}

상수 인터페이스 안티패턴은 인터페이스를 잘못 사용한 예다.

클래스 내부에서 사용하는 상수는 외부 인터페이스가 아니라 내부 구현에 해당한다. 따라서 상수 인터페이스를 구현하는 것은 이 내부 구현을 클래스의 API로 노출하는 행위다.

이 행위는 사용자에게 혼란을 주거나, 클라이언트 코드가 내부 구현에 해당하는 이 상수들에 종속될 수 있다.

잘못된 예시에는 java.io.ObjectStreamConstants 등, 자바 플랫폼 라이브러리에도 몇몇가지가 있다.

상수를 정의하는 방법

상수를 공새할 목적이라면 몇가지 합당한 방법이 있다. 특정 클래스나 인터페이스와 강하게 연관된 상수라면 클래스나 인터페이스 자체에 추가해야한다. Integer와 Double에 선언된 MIN_VALUE, MAX_VALUE가 그 예시이다.

열거 타입으로 나타내기 적합한 상수라면 열거 타입으로 만들어 공개하면된다. 그것도 아니라면 인스턴스화 할 수 없는 유틸리티 클래스에 담아 공개한다.

아래는 유틸리티 클래스의 예시이다.

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

    // 아보가드로 수 (1/몰)
    static final double AVOGADROS_NUMBER = 6.022_140_857e23;
    // 볼츠만 상수 (J/K)
    static final double BOLTZMANN_CONSTANT = 1.380_648_52e-23;
    // 전자 질량 (kg)
    static final final double ELECTRON_MASS = 9.109_383_56e-31;
}

보통 유틸리티 클래스에 있는 상수를 사용하기 위해서는 PhysicalConstants.AVOGADROS_NUMBER 와같이 클래스 이름까지 함께 명시해야 한다.

하지만 빈번이 사용한다면 정적 임포트(static import)하여 클래스 이름은 생략할 수 있다.

import static com.mho.common.PhysicalConstants.*;

public class TestClass {
    double atoms(double mols) {
        return AVOGADROS_NUMBER * mols;
    }
  ...
}