[effective java] 16. public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라

업데이트:

요약

public 클래스의 가변 필드를 직접 노출하지 않도록, 필드를 모두 private으로 바꾸고 public 접근자(getter)를 추가하자.

예시0

인스턴스 필드를 모아둔 것 외에는 목적이 없는 퇴보한 클래스

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

예시1

class Point {
    private double x;
    private double y;
    
    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
    
    public double getX() {return x;}
    public double getY() {return y;}
}

예시2

import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Getter //접근
@NoArgsConstructor
@Entity
public class Product extends Timestamped{

    @GeneratedValue(strategy = GenerationType.AUTO)
    @Id
    private Long id;

    @Column(nullable = false)
    private String title;

    @Column(nullable = false)
    private String image;

    @Column(nullable = false)
    private String link;

    @Column(nullable = false)
    private int lprice;

    @Column(nullable = false)
    private int myprice;

    public Product(ProductRequestDto requestDto) {
        this.title = requestDto.getTitle();
        this.link = requestDto.getLink();
        this.lprice = requestDto.getLprice();
        this.image = requestDto.getImage();
        this.myprice = 0;
    } 

    public void updateByItemDto (ItemDto itemDto) {
        this.lprice = itemDto.getLprice();
    }

    public void update(ProductMypriceRequestDto requestDto) {
        this.myprice = requestDto.getMyprice();
    }
}

문제점

  • 필드에 직접 접근 가능 (캡슐화X)
  • API를 수정하지 않고는 내부표현 변경 불가
  • 불변식 보장 불가
  • 외부에서 필드 접근 시 부수 작업 수행 불가

예외

  • packge-private
  • private 중첩 클래스

    각각 패키지/클래스 내부에서만 동작하는 코드이기 때문에 데이터 필드가 노출되어도 무관 (오히려 접근자 방식보다 훨씬 깔끔)

private 멤버를 선언한 클래스 내부에서만 접근 가능

package-private 멤버가 소속된 패키지 내부의 모든 클래스에서 접근 가능

protected package-private의 접근 범위 포함. 멤버를 선언한 클래스의 하위 클래스에서도 접근 가능

public 모든 곳에서 접근 가능

기존 사례

java.awt.package의 Point와 Dimension 클래스 (아이템67)

  • 필드를 외부로 직접 노출
  • 불변성을 보장할 수 없음

불변식 예시

문제점

  • API를 변경하지 않고는 내부표현 변경 불가
  • 외부에서 필드 접근 시 부수 작업 수행 불가
    //각 인스턴스가 유효한 시간을 표현함을 보장 (final)
    public final class Time {
      private static final int HOURS_PER_DAY = 24;
      private static final int MINUTES_PER_HOURS = 60;
    
      public final int hour;
      public final int minute;
    
      public Time(int hour, int minute) {
          if (hour < 0 || hour >= HOURS_PER_DAY)
              throw new IllegalArgumentException("시간: " + hour);
          if (minute < 0 || minute >= MINUTES_PER_HOURS)
              throw new IllegalArgumentException(": " + minute);
          this.hour = hour;
          this.minute = minute;
      }
      //..생략
    }
    

#핵심정리

public 클래스는 절대 가변 필드를 직접 노출하지 않도록 한다.

불변 필드라면 노출해도 위험이 줄지언정 완전 안심은 할 수 없다.

예외적으로 package-private, private 중첩 클래스에서는 불변/가변 여부와 관계없이 필드를 노출하는 편이 나은 경우가 있다.

출처: 이펙티브 자바 3판, https://velog.io/@yebink/이펙티브-자바-4장.-클래스와-인터페이스

업데이트:

댓글남기기