이번 주제는 public으로 선언한 클래스에서 필드변수를 어떻게 선언해야 하는지에 대한 포스팅이다.

이전 포스팅에서 이번 주제에 대해 잠깐 설명했었다.

객체 필드변수는 절대로 public으로 선언하면 안 된다. 필드를 public으로 선언하면 필드에 저장될 값을 제한할 수 없게 되어 그 필드에 관계된 불변식을 강제할 수 없다. 그러므로 getter, setter와 같은 접근자 메서드를 사용하는 게 좋다.

좀 더 자세히 알아보자.

접근자 메서드

public class PositivePointWithPublicField {
	// Can not limit value
	public double x;
	public double y;

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

위 클래스는 x 와 y의 값이 모두 양의 값인 점을 표현하는 클래스이다. 하지만 x 와 y는 public이므로 클라이언트는 직접 조작할 수 있어 값이 음의 값이 될 수 있다. 또한 필드를 사용하는 순간에 어떤 동작이 실행되도록 만들 수도 없다. 이런 클래스는 아래와 같이 변경해주어야 한다.

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

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

	public double getX() {
		// Any action can be performed
		return x;
	}

	public void setX(double x) {
		// Can limit value
		if (x <= 0) {
			throw new IllegalArgumentException();
		}
		this.x = x;
	}

	public double getY() {
		// Any action can be performed
		return y;
	}

	public void setY(double y) {
		// Can limit value
		if (y <= 0) {
			throw new IllegalArgumentException();
		}

		this.y = y;
	}
}

setter 메서드를 이용하여 필드의 값을 강제할 수 있으며 getter 메서드를 이용하여 필드를 사용하는 순간에 특정 동작을 수행하도록 할 수 있다.

package-private 클래스나 private 중첩 클래스는 public으로 선언해도 상관없다고 한다. 클래스가 정확히 어떤 클래스인지 기술만 한다면 말이다. 책 내용을 정확히 이해하지 못했지만, package-private 클래스나 private 중첩 클래스는 클라이언트에게 공개하는 API가 아니라 API를 구현하기 위한 클래스이므로 API 개발자가 잘만 쓰면 상관없다는 의미인 것 같다.

자바 클래스에서는 이 주제의 규칙을 지키지 않는 클래스들이 있다. 대표적으로 Point와 Dimension 클래스이다. 이런 클래스는 참고하지 않는 것이 좋다.

public class Point extends Point2D implements java.io.Serializable {
    public int x;
    public int y;
     
    // ...
}

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

link to Rule15.