본문으로 건너뛰기
안동민 개발노트 아이콘

안동민 개발노트

본문 시작
2장 : 변수와 데이터 타입

상수와 변수의 유효 범위

값이 한 번 정해진 뒤 바뀌지 않아야 한다면 그 의도를 코드가 직접 표현해야 합니다.

final 지역 변수와 필드는 한 번만 대입할 수 있지만 객체 자체를 깊게 불변으로 만들지는 않습니다.

블록 범위는 이름 충돌을 막는 장치이면서 값의 수명을 줄이는 장치이기도 합니다.

클래스 상수는 보통 static final로 한 번만 보관합니다.

이 절에서는 재대입 금지, 객체 변경, 이름의 범위를 서로 구분합니다.


final은 재대입을 막는다

기본 타입 변수에는 값 변경을, 참조 변수에는 다른 객체를 가리키는 변경을 막습니다.

참조가 가리키는 가변 객체의 내부 상태까지 자동으로 고정하지는 않습니다.


상수는 클래스 전체에서 한 번만 둔다

모든 인스턴스가 공유하는 변하지 않는 값은 static final 필드가 적합합니다.

이름은 대문자와 밑줄을 쓰는 관례가 의도를 빠르게 전달합니다.

FinalBoundaries.java에서는 지역 변수의 재대입 금지와 객체 내부 변경 가능성을 따로 관찰합니다. final 참조가 가리키는 객체까지 자동으로 불변이 되는 것은 아니라는 점이 핵심입니다.

예제의 List 사용법은 11장에서 다시 배웁니다. 여기서는 names.add(...)로 참조 대상의 내용은 바뀌지만 names = ...로 참조 자체를 바꾸지는 못한다는 두 줄의 차이에만 집중해도 충분합니다.

FinalBoundaries.java
import java.util.ArrayList;
import java.util.List;

public class FinalBoundaries {
    static final int MAX_RETRIES = 3;

    public static void main(String[] args) {
        final List<String> names = new ArrayList<>();
        names.add("Ada"); // 객체 내부 변경은 가능
        System.out.println(names);
        System.out.println("limit=" + MAX_RETRIES);

        // names = new ArrayList<>(); // 컴파일 오류: final 참조 재대입
    }
}

MAX_RETRIES는 객체를 만들지 않아도 클래스 이름으로 읽을 수 있고 클래스 로딩 단위로 하나만 존재합니다. 인스턴스마다 다른 값이어야 한다면 static을 빼고 생성자에서 받는 필드로 모델링해야 합니다.

[Ada]
limit=3

final 참조와 불변 컬렉션은 서로 다른 보장입니다. 재대입과 내부 변경을 나누어 보세요.


블록은 이름의 가시성을 제한한다

조건문과 반복문의 중괄호 안에서 선언한 지역 변수는 블록 밖에서 사용할 수 없습니다.

필요한 곳보다 넓은 범위에 변수를 두지 않는 것이 좋습니다.

안쪽 블록의 message는 블록을 벗어나는 순간 이름을 찾을 수 없습니다. 임시 계산값을 필요한 블록 안에 두면 이후 코드에서 잘못 재사용할 여지가 줄어드므로, 유효 범위는 단순한 문법 제한이 아니라 오류를 막는 설계 도구입니다.

if (true) {
    int localOnly = 10;
    System.out.println(localOnly);
}
// System.out.println(localOnly); // 컴파일 오류: 블록 범위 밖

List<String> immutable = List.copyOf(java.util.List.of("Ada"));
// immutable.add("Linus"); // 실행 시 UnsupportedOperationException

상수 설계는 재대입만 막는지 객체 상태까지 고정하는지, 이름이 어느 범위에서 보여야 하는지를 나누어 결정해야 합니다.