인스턴스 필드만을 모아놓은 클래스는 데이터에 직접 접근할 수 있으나 캡슐화의 이점을 제공하지 못한다.
[인스턴스 필드만을 모아놓은 클래스]
public class Point {
public double x;
public double y;
}
객체지향적으로 클래스를 작성하기 위해서는 필드를
private
으로 모두 바꾸고public
접근자(getter)를 추가한다.
[public 접근자를 추가한 클래스 작성]
public class Point {
public double x;
public double y;
public Point(final double x, final double y) {
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public void setX(final double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(final double y) {
this.y = y;
}
}
package-private
클래스 혹은private
중첩 클래스라면 데이터 필드를 노출한다 해도 클래스가 표현하려는 추상 개념만 올바르게 표현해주면 문제가 없다.
public class ColorPoint {
private static class Point{
public double x;
public double y;
}
public Point getPoint(){ // 클라이언트 코드가 클래스 내부에 묶인다.
Point point = new Point(); //ColorPoint 외부에서는
point.x = 1.2; // Point 클래스 내부 조작이 불가능하다.
point.y = 5.3;
return point;
}
}
private
(또는 package-private
) 클래스를 중첩시키면 탑 클래스(제일 바깥 클래스) 외부에서는 Point
클래스의 필드에 접근하는 것이 불가능하다. 하지만, ColorPoint
에서는 Point
클래스 필드 조작이 가능하다.private
중첩 클래스의 경우 수정 범위가 더 좁아져서 이 클래스를 포함하는 외부 클래스까지로 제한된다.자바 플랫폼 라이브러리에도
public
클래스의 필드를 직접 노출시키지 말라는 규칙을 어긴 사례가 존재한다. 대표적으로java.awt.package
클래스의Point
와Dimension
클래스가 있다.
public
클래스 필드가 불변이라면 직접 노출할 떄의 단점이 조금은 줄어들지만, 여전히 좋은 코드가 아니다.public
필드에 final
키워드를 추가해 불변으로 만들면 불변식은 보장할 수 있게 되지만 여전히 API를 변경하지 않고는 표현 방식을 바꿀 수 없고 필드를 읽을 때 부수작업을 수행할 수 없다는 단점은 변하지 않는다.[불변 필드를 노출한 public 클래스]
public class Time {
private static final int HOURS_PER_DAY = 24;
private static final int MINUTES_PER_HOUR = 60;
public final int hour;
public final int minute;
public Time(final int hour, final int minute) {
this.hour = hour;
this.minute = minute;
}
}
...
public
클래스는 절대 가변 필드를 직접 노출해서는 안된다. 하지만 package-private
클래스나 private
중첩 클래스에서는 종종 필드를 노출하는 편이 나을 때도 있다.