변수 선언과 초기화
변수는 값을 담는 상자라기보다 특정 타입의 값을 특정 범위에서 가리키는 이름입니다.
Java는 지역 변수를 읽기 전에 반드시 초기화하도록 컴파일 단계에서 검사합니다.
필드에는 기본값이 있지만 지역 변수에 같은 규칙을 기대하면 오류가 납니다.
var는 타입을 없애는 문법이 아니라 초기값으로 정적 타입을 추론하는 문법입니다.
이 절에서는 선언 위치와 초기화 시점이 코드의 신뢰도에 어떤 영향을 주는지 살펴봅니다.
선언은 타입과 사용 범위를 고정한다
지역 변수는 선언된 블록 안에서만 보입니다.
필요한 지점 가까이에 선언하면 아직 유효하지 않은 상태로 존재하는 시간을 줄일 수 있습니다.
초기화되지 않은 지역 변수는 읽을 수 없다
컴파일러의 확정 할당 분석은 모든 실행 경로에서 값이 지정됐는지 확인합니다.
조건 한쪽에서만 값을 넣으면 다른 경로를 설명하지 못해 컴파일 오류가 납니다.
지역 변수의 확정 할당 검사는 실행 전에 모든 경로를 살펴봅니다. 조건문 한쪽에서만 값을 넣으면 실제로 그 경로가 자주 실행되더라도 다른 경로에서 읽힐 가능성이 있어 컴파일되지 않습니다.
public class VariableLifetime {
public static void main(String[] args) {
int attempts;
boolean retry = true;
if (retry) {
attempts = 1;
} else {
attempts = 0;
}
var message = "attempts=" + attempts; // 정적 타입은 String
System.out.println(message);
// int missing;
// System.out.println(missing); // 컴파일 오류: 초기화되지 않은 지역 변수
}
}예제의 대입을 if 한쪽 가지 안으로 옮겨 보면 컴파일러가 어느 지점에서 초기화를 증명하지 못하는지 알 수 있습니다. 무의미한 0을 먼저 넣어 오류를 숨기기보다 모든 경로에서 의미 있는 값을 정하도록 분기를 고치는 편이 안전합니다.
attempts=1모든 if 경로에서 attempts를 대입하므로 확정 할당 검사를 통과합니다.
var는 읽기 쉬울 때만 쓴다
오른쪽 표현식만 보고 타입이 명확한 지역 변수에는 var가 중복을 줄입니다.
숫자 리터럴이나 복잡한 팩터리처럼 실제 타입이 숨는 곳에서는 명시 타입이 더 낫습니다.
var는 동적 타입이 아니라 오른쪽 식에서 정적 타입을 추론하는 문법입니다. var users = loadUsers()처럼 이름과 생성식이 타입을 충분히 말할 때는 짧아지지만, 숫자 리터럴이나 낯선 반환 타입에서는 타입을 직접 적는 편이 독자에게 친절합니다.
아래 List와 Map의 제네릭 문법은 10장과 11장에서 자세히 다룹니다. 지금은 컬렉션 동작을 외우기보다 var를 쓴 첫 줄과 타입을 직접 적은 둘째 선언 가운데 어느 쪽이 오른쪽 식만으로 더 쉽게 읽히는지만 비교합니다.
var names = java.util.List.of("Ada", "Linus"); // List<String>이 명확함
java.util.Map<String, java.util.List<Integer>> scores =
new java.util.HashMap<>(); // 공개할 계약을 명시
// var value; // 컴파일 오류: 초기값이 없어 추론 불가
// var nothing = null; // 컴파일 오류: null만으로 타입을 정할 수 없음하나의 변수에는 하나의 의미를 준다
같은 변수를 단계마다 다른 뜻으로 재사용하면 타입이 같아도 코드를 추적하기 어렵습니다.
의미가 바뀌면 새 이름을 만들고 범위를 좁히는 편이 안전합니다.
변수 이름을 재사용해 서로 다른 단위를 담으면 타입이 같아도 오류를 막기 어렵습니다. value 하나에 원화와 달러를 번갈아 넣지 말고 priceInWon, priceInDollar처럼 의미와 수명을 좁혀 보세요.
좋은 변수 선언은 타입뿐 아니라 언제부터 유효하고 어디까지 같은 의미를 유지하는지를 드러냅니다.