[교재 EffectiveJava] 아이템 16. public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라

반응형
728x90
반응형

public 클래스의 public 필드의 사용(하지마라)

public class Point {
    public double x;
    public double y;

    public static void main(String[] args) {
        Point point = new Point();
        // 외부 클래스에서 아래와같이 직접 접근 가능
        point.x = 10;
        point.y = 20;

        System.out.println(point.x);
        System.out.println(point.y);
    }
}

 

위 예제의 public 필드는 아주 위험하다. 이런 클래스는 데이터 필드에 직접 접근이 가능하게되면서, API를 수정하지 않고는 내부 표현을 바꿀 수 없고, 불변식을 보장할 수 없고, 외부에서 필드에 접근할때 부수 작업을 수행할 수도 없다. 이를 아래의 private 필드의 사용 예제처럼 변경해야한다.

 

public 필드 사용시 문제가 없는 경우

package-private(=default) 클래스 혹은 private 중첩 클래스라면 데이터 필드를 노출한다해도 문제가 없다. 클라이언트 코드가 이 클래스를 사용한다 해도, 이 클래스를 포함하는 패키지 안에서만 동작하는 코드이므로, 패키지 바깥 코드에서는 데이터 표현 방식을 변경할 수 있다. private 중첩 클래스의 경우라면 수정 범위가 더 좁아져서 이 클래스를 포함하는 외부 클래스까지로 제한된다.

 

 

public 클래스의 private 필드의 사용(하라)

public class PointPrivate {
    private double x;
    private double y;

    public PointPrivate(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public double getX() {
        return x;
    }

    public void setX(double x) {
        this.x = x;
    }

    public double getY() {
        return y;
    }

    public void setY(double y) {
        this.y = y;
    }
}

 

필드를 모두 private 로 변경하고, public 접근자 (getter/setter) 을 추가하였다. public 클래스에서라면 이 방식을 사용해야한다. 패키지 바깥에서 접근할 수 있는 클래스라면 접근자를 제공함으로써 클래스 내부 표현 방식을 언제든 바꿀 수 있는 유연성을 얻을 수 있다.

 

예외적으로 java.awt.package 패키지의 Point 와 Dimension 클래스(성능 이슈 존재)는 public 클래스임에도 public 필드를 사용중이다. 이 클래스들과 상관없이 우리는 public 클래스의 필드를 직접 노출하지 말라는 규칙을 지켜야한다.

 

Dimension.java
public class Dimension extends Dimension2D implements java.io.Serializable {
    public int width;
    public int height;
    
    ...
}

 

 

public final 필드

public class PointFinal {
    private static final int HOURS_PER_DAY = 24;
    private static final int MINUTES_PER_HOUR = 60;

    public final int hour;
    public final int minus;

    /** 유효한 시간임을 보장 */
    public PointFinal(int hour, int minus) {
        if (hour < 0 || hour >= HOURS_PER_DAY) {
            throw new IllegalArgumentException("시간 : " + hour);
        }
        if (minus < 0 || minus >= MINUTES_PER_HOUR) {
            throw new IllegalArgumentException("분 : " + minus);
        }
        this.hour = hour;
        this.minus = minus;
    }
}

 

public 클래스가 필드를 공개하면 이를 사용하는 클라이언트가 생겨날 것이므로 내부 표현 방식을 마음대로 바꿀 수 없게된다. public 필드가 불변이라도 직접 노출할때의 단점은 불변성 외에는 모두 여전히 존재한다. 

 

public final 로 선언된 필드는 인스턴스 생성 시점에서만 값이 셋팅된다. 생성자에 if 제어문을 추가하여 유효한 시간임을 보장할 수 있다.

 

 

 

반응형

Designed by JB FACTORY