CodeGym /행동 /JAVA 25 SELF /Getter와 Setter: 문법, 모범 사례

Getter와 Setter: 문법, 모범 사례

JAVA 25 SELF
레벨 15 , 레슨 2
사용 가능

1. Getter와 Setter란?

객체를 금고라고 생각해 봅시다. 금고의 비공개 필드는 금고 안의 내용물이고, getter와 setter는 각 칸의 열쇠입니다. getter는 안에 무엇이 있는지 알려 주고, setter는 그곳에 새로운 것을 조심스럽게 넣게 해 줍니다(하지만 문서 대신 고슴도치를 넣는 일은 피합시다).

Getter

Getterprivate 필드 값을 반환하는 public 메서드입니다. 보통 이름은 get + 필드 이름의 첫 글자를 대문자로 시작합니다.

public class Person {
    private String name; // 비공개 필드

    // name 필드의 getter
    public String getName() {
        return name;
    }
}

Setter

Setter는 비공개 필드의 값을 변경할 수 있게 하는 public 메서드입니다. 이름은 set + 필드 이름의 첫 글자를 대문자로 시작합니다.

public class Person {
    private String name;

    // name 필드의 setter
    public void setName(String name) {
        this.name = name;
    }
}

boolean 필드의 경우

boolean 타입 필드의 경우 getter에 is 접두사를 사용하는 것이 관례입니다:

private boolean active;

public boolean isActive() {
    return active;
}

public void setActive(boolean active) {
    this.active = active;
}

2. 문법과 명명 규칙

Java는 엄격하지만 꽉 막힌 언어는 아닙니다. 코드가 다른 개발자(그리고 한 달 뒤의 여러분)에게도 이해되도록 해 주는 확립된 규칙이 있습니다.

  • Getter: public Type getFieldName()
  • Setter: public void setFieldName(Type value)
  • boolean의 Getter: public boolean isFieldName()

필드 이름은 메서드에서 첫 글자를 대문자로 씁니다. 예를 들어 필드가 age라면 메서드는 getAge()setAge()가 됩니다.

이 규칙은 JavaBeans 스타일을 따르며, 덕분에 IDE, 라이브러리, 프레임워크가 여러분의 getter와 setter를 자동으로 찾아낼 수 있습니다. 예를 들어 Spring이나 JavaFX를 사용한다면, 필요한 순간에 이러한 메서드가 마치 "마법"처럼 호출됩니다.

3. 코드 예시

학습용 프로젝트로 간단한 "연락처" 애플리케이션(전화번호부와 유사)을 가져와서 올바른 getter와 setter를 추가해 보겠습니다.

예시: 비공개 필드와 getter/setter를 가진 Contact 클래스

public class Contact {
    private String name;
    private String phone;
    private int age;
    private boolean favorite;

    // Getter들
    public String getName() {
        return name;
    }

    public String getPhone() {
        return phone;
    }

    public int getAge() {
        return age;
    }

    public boolean isFavorite() {
        return favorite;
    }

    // Setter들
    public void setName(String name) {
        this.name = name;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public void setAge(int age) {
        // 검증 예시: 나이는 음수일 수 없음
        if (age < 0) {
            System.out.println("나이는 음수가 될 수 없습니다!");
            return;
        }
        this.age = age;
    }

    public void setFavorite(boolean favorite) {
        this.favorite = favorite;
    }
}

애플리케이션에서의 사용

Contact friend = new Contact();
friend.setName("이반 이바노프");
friend.setPhone("+1-999-123-45-67");
friend.setAge(25);
friend.setFavorite(true);

System.out.println("이름: " + friend.getName());
System.out.println("전화: " + friend.getPhone());
System.out.println("나이: " + friend.getAge());
System.out.println("즐겨찾기: " + (friend.isFavorite() ? "예" : "아니오"));

setter에서의 검증 예시

setAge의 setter에서 간단한 검사를 추가한 점에 주목하세요. 나이가 음수이면 필드를 변경하지 않고 경고를 출력합니다. 이는 객체를 잘못된 데이터로부터 보호하는 간단한 방법입니다.

4. 모범 사례: 올바르게 작성하는 방법

모든 필드에 public setter가 필요하지는 않습니다

때로는 필드가 읽기 전용이어야 합니다. 예를 들어, 객체 생성 시 설정되고 이후 변경되지 않는 고유 식별자 같은 경우입니다. 이런 경우에는 setter를 작성하지 않습니다:

public class Contact {
    private final int id; // 초기화 이후에는 변경 불가

    public Contact(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    // setId는 없습니다!
}

접근 제어와 검증을 위해 getter/setter를 사용하세요

public void setName(String name) {
    if (name == null || name.trim().isEmpty()) {
        System.out.println("이름은 비어 있을 수 없습니다!");
        return;
    }
    this.name = name;
}

내부의 변경 가능한 객체를 직접 노출하지 마세요

예를 들어, 전화번호 목록 같은 필드가 있다면:

private String[] phones;

이를 getter로 그대로 반환해서는 안 됩니다:

public String[] getPhones() {
    return phones; // 좋지 않음!
}

이런 코드는 외부에서 목록을 마음대로 변경할 수 있게 하여 캡슐화를 깨뜨립니다!

더 나은 방법: 배열의 복사본을 반환하세요:

public String[] getPhones() {
    return Arrays.copyOf(phones, phones.length); // 복사본을 반환
}

또는 간단히 clone:

public String[] getPhones() {
    return phones.clone(); 
}

getter와 setter는 이해하기 쉽고 단순하게

  • 복잡한 비즈니스 로직을 getter/setter에 넣지 마세요. 이들의 역할은 단순합니다: 접근 제어와 필요 시 검증.
  • 변경되어서는 안 되는 필드는 아예 setter를 만들지 마세요.
  • 외부에서 접근할 수 없어야 하는 필드는 getter를 만들지 마세요.

5. IDE에서 getter/setter 자동 생성

필드가 열 개쯤 되는 클래스를 수동으로 작성하는 일은 꽤 번거롭습니다. 다행히 현대적인 IDE(IntelliJ IDEA, Eclipse, 플러그인을 사용한 VS Code 등)는 이를 자동으로 생성해 줍니다.

IntelliJ IDEA에서

  1. 클래스를 열고, 클래스 본문 안에 커서를 둡니다.
  2. Alt + Insert(또는 Code -> Generate...)를 누릅니다.
  3. Getter and Setter를 선택합니다.
  4. 원하는 필드를 체크하고 OK를 누릅니다.

짜잔! getter와 setter가 자동으로 생성됩니다.

Eclipse에서

  1. 클래스를 엽니다.
  2. 마우스 오른쪽 버튼 클릭 — SourceGenerate Getters and Setters...
  3. 필드를 선택하고 OK를 누릅니다.

VS Code(Java Extension Pack 사용)에서

  1. 클래스 파일을 엽니다.
  2. 명령 팔레트(Ctrl+Shift+P)에서 Generate getters and setters를 입력합니다.
  3. 안내에 따라 진행합니다.

6. 애플리케이션 개선: 캡슐화 실전

이전 강의에서는 연락처를 저장하는 간단한 애플리케이션을 만들었습니다. 이제 필드를 비공개로 만들고 getter/setter를 통해서만 접근하도록 개선해 보겠습니다.

이전(나쁜 예):

public class Contact {
    public String name;
    public String phone;
    public int age;
}

문제: 아무 코드나 이렇게 할 수 있습니다:

Contact c = new Contact();
c.age = -1000; // 이제 우리 전화번호부에 뱀파이어가 생겼습니다!

이후(좋은 예):

public class Contact {
    private String name;
    private String phone;
    private int age;

    public void setAge(int age) {
        if (age < 0) {
            System.out.println("나이는 음수가 될 수 없습니다!");
            return;
        }
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    // 나머지 getter/setter...
}

이제는 외부에서 우연히(혹은 의도적으로) 객체를 망가뜨리기 어렵습니다.

7. 계산되거나 변경 불가능한 속성의 getter/setter

값을 필드에 저장하지 않고, 즉시 계산하는 경우도 있습니다:

public class Rectangle {
    private int width;
    private int height;

    public int getArea() {
        return width * height;
    }
}

면적에 대한 setter는 필요 없습니다 — 직접 설정할 수 없고, 너비나 높이를 변경해야만 바뀝니다.

8. Getter와 Setter: 흔한 실수

실수 №1: getter/setter가 캡슐화를 깨뜨림.
getter가 내부의 변경 가능한 객체(예: 리스트)에 대한 참조를 반환하면, 외부 코드가 모든 검사를 우회해 이를 변경할 수 있습니다. 이는 캡슐화의 취지에 어긋납니다.

실수 №2: setter가 데이터를 검증하지 않음.
setter가 아무 검사 없이 값을 할당하기만 하면, 잘못된 객체 상태(예: 음수 나이, 빈 이름)를 초래할 수 있습니다.

실수 №3: 모든 필드에 대해 setter를 자동 생성.
IDE가 모든 필드에 대한 setter를 생성해 줄 수 있지만, 항상 옳은 것은 아닙니다! 예를 들어 식별자(id)에는 setter가 필요 없습니다.

실수 №4: getter/setter에 복잡한 로직을 넣음.
getter와 setter는 단순해야 합니다. 복잡한 비즈니스 로직이 필요하다면 별도의 메서드로 분리하세요.

실수 №5: 명명 규칙을 어김.
getter를 getName() 대신 fetchName()으로 이름 지으면, 일부 프레임워크와 라이브러리는 이를 인식하지 못할 수 있습니다.

코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION