Home Item 22 - 인터페이스는 타입을 정의하는 용도로만 사용하라
Post
Cancel

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

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

인터페이스는 자신을 구현한 클래스의 인스턴스를 참조할 수 있는 타입 역할을 한다.
클래스가 어떤 인터페이스를 구현한다는건, 인스턴스로 무엇을 할 수 있는지를 클라이언트를 알려주는 행위

안티패턴 - 사용 금지

메서드 없이, 상수를 뜻하는 static final 필드로만 가득찬 인터페이스

1
2
3
4
5
6
7
8
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;
}

문제점

  • 클래스 내부에서 사용하는 상수는 외부 인터페이스가 아닌 내부 구현이다.
    • 내부 구현을 클래스의 API로 노출하는 행위가 됨.
      1
      2
      
      // 인터페이스에서 protected도 불가!! : 제어자 'protected'은(는) 허용되지 않습니다
      protected static final double AVOGADROS_NUMBER = 6.002_140_857e23;
      
  • 사용자에게 혼란을 줌.
    • 사용자에게는 아무런 의미가 없다.
  • 다음 릴리즈에 사용하지 않더라도 바이너리 호환성을 위해 여전히 상수 인터페이스를 구현하고 있어야 한다.
    • 다음 릴리즈에 call에서 AVOGADROS_NUMBER를 사용하고 있지 않더라도 삭제하면 main의 myResult 할당부에서 에러 발생
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Calculator implements PhysicalConstants {
    public double call(int input){
        return input * AVOGADROS_NUMBER;
    }
    public double call2(int input){
        return input * BOLTZMANN_CONSTANT;
    }
    public double call3(int input){
        return input * ELECTRON_MASS;
    }

    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        double myResult = 100 * Calculator.AVOGADROS_NUMBER;
        System.out.println(myResult);
    }
}
  • final이 아닌 클래스가 상수 인터페이스를 구현한다면 모든 하위 클래스의 이름공간이 그 인터페이스가 정의한 상수들로 오염되어 버린다.

대안

상수를 공개할 목적이라면 몇 가지 대안이 있다.
1. 클래스나 인터페이스 자체에 추가
ex) Integer와 Double에 선언된 MIN_VALUE와 MAX_VALUE 상수

1
2
3
4
public final class Integer extends Number implements Comparable<Integer>, Constable, ConstantDesc {
    @Native public static final int MIN_VALUE = 0x80000000;
    @Native public static final int MAX_VALUE = 0x7fffffff;
}

2. 상수 유틸리티 클래스 제공

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class PhysicalConstants {
    //인스턴스화 방지
    private 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;
}

public class Calculator{
    public double call(int input){
        return input * PhysicalConstants.AVOGADROS_NUMBER;
    }
    public double call2(int input){
        return input * PhysicalConstants.BOLTZMANN_CONSTANT;
    }
    public double call3(int input){
        return input * PhysicalConstants.ELECTRON_MASS;
    }

    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        double myResult = 100 * PhysicalConstants.AVOGADROS_NUMBER;
        System.out.println(myResult);
    }
}

3. enum 활용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public enum PhysicalConstants {
    AVOGADROS_NUMBER(6.022_140_857e23),
    BOLTZMANN_CONSTANT(1.380_648_52e-23),
    ELECTRON_MASS(9.109_383_56e-31);

    private double value;

    PhysicalConstants(double value) {
        this.value = value;
    }

    public double getValue(){
        return value;
    }
}

public class Calculator{
    public double call(int input){
        return input * PhysicalConstants.AVOGADROS_NUMBER.getValue();
    }
    public double call2(int input){
        return input * PhysicalConstants.BOLTZMANN_CONSTANT.getValue();
    }
    public double call3(int input){
        return input * PhysicalConstants.ELECTRON_MASS.getValue();
    }

    public static void main(String[] args) {
        Calculator calculator = new Calculator();
        double myResult = 100 * PhysicalConstants.AVOGADROS_NUMBER.getValue();
        System.out.println(myResult);
    }
}
This post is written by PRO.

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

Item 23 - 태그 달린 클래스보다는 클래스 계층구조를 활용하라