Item57. 지역변수의 범위를 최소화 하라
지역변수의 유효 범위를 최소로 줄이면 코드 가독성과 유지보수성이 높아지고 오류 가능성은 낮아진다.
지역변수의 범위를 줄이는 가장 강력한 기법은 '가장 처음 쓰일 때 선언하기'다.
- 사용하려면 멀었는데 미리 선언부터 하면 코드가 어수선해져 가독성이 떨어진다.
- 변수를 실제 사용하는 시점엔 타입과 초깃값이 기억나지 않을 수 있다.
- 지역변수를 생각 없이 선언하다 보면 변수가 쓰이는 범위보다 너무 앞서 선언하거나, 다 쓴 뒤에도 여전히 살아있게 되기 쉽다.
- 지역변수의 범위는 선언된 지점부터 그 지점을 포함한 블록이 끝날 때 까지이므로, 실제 사용하는 블록 바깥에 선언된 변수는 그 블록이 끝난 뒤까지 살아있게 되어 실수로 의도한 범위 앞 혹은 뒤에서 그 변수를 사용하면 끔찍한 결과로 이어질 수 있다.
거의 모든 지역변수는 선언과 동시에 초기화해야 한다.
- 초기화에 필요한 정보가 충분하지 않다면 충분해질 때까지 선언을 미뤄야 한다.
- try-catch 문은 이 규칙에서 예외다. 변수를 초기화하는 표현식에서 검사 예외를 던질 가능성이 있다면 try 블록 안에서 초기화해야 한다. (그렇지 않으면 예외가 블록을 넘어 메서드에까지 전파된다.)
- 변수값을 try 블록 바깥에서도 사용해야 한다면 try 블록 앞에서 선언해야 한다.
반복문은 톡특한 방식으로 변수 범위를 최소화해준다.
for나 for-each 반복문에서는 반복 변수(loop variable)의 범위가 반목분의 몸체, for 키워드와 몸체 사이의 괄호 안으로 제한된다. 따라서 반복 변수의 값을 반복문이 종료된 뒤에도 써야 하는 상황이 아니라면 while문 보다 for문을 쓰는편이 낫다.
- 컬렉션이나 배열을 순회하는 권장 관용구
for (Element e: c) {
... // e로 무언가를 한다.
}
- 반복자가 필요할 때의 관용구
for (Iterator<Element> i = c.iterator(); i.hasNext();) {
Element e = i.next();
... // e와 i로 무언가를 한다.
}
- while문 보다 for 문이 더 나을 때
Iterator<Element> i = c.iterator();
while(i.hasNaxt()) {
doSomething(i.next());
}
...
Iterator<Element> i2 = c2.iterator();
while(i.hasNaxt()) { // 버그
doSomething(i2.next());
}
복붙할 때 i는 이미 첫번째 와일문에서 다 소비한 상태인데 두번째 와일문에서 i를 다시 사용하고 있다.
i2를 사용했어야 했다.
그럼에도 불구하고 i의 유효범위가 끝나지 않아 컴파일도 잘 되고 실행 시 예외도 던지지 않는다.
하지만 c2를 순회하는 두번째 와일문은 조건식이 충족되지 않으니 실행 되지 않는다.
프로그램 오류가 겉으로 드러나지 않으니 오랜 기간 발견되지 않을 수 있다.
for문을 사용하면 이러한 오류를 컴파일 타임에 잡아준다.
for (Iterator<Element> i = c.iterator(); i.hasNext();) {
Element e = i.next();
... // e와 i로 무언가를 한다.
}
// 다음 코드는 "i를 찾을 수 없다"는 컴파일 오류를 낸다.
for(Itertator<Element> i2 = c2.iterator(); i.hasNext();) {
Element e2 = i2.next();
... // e2와 i2로 무언가를 한다.
}
첫 번째 반복문이 사용한 원소와 반복자의 유효 범위가 반복문 종료와 함께 끝나기 때문이다.
for 문이 복붙 오류를 줄여주는 이유중 또 한가지는 변수 유효 범위가 for문 범위와 일치하여 같은 이름의 변수를 여러번 반복문에서 써도 서로 아무런 영향을 주지 않는다.
for문의 장점을 더 이야기 하면 while문보다 짧아서 가독성이 좋다.
for(int i = 0, n = expensiveConputation(); i < n; i++) {
... // i로 무언가 한다.
}
반복 여부를 결정짓는 변수 i의 한계값을 변수 n에 저장하여 반복때마다 다시 계산해야 하는 비용을 없앴다.
계산처리 비용이 비싼 한계값이라면 매번 반복할때마다 변수 i와 비교하기 위해 사용했던 계산식(array.length 같은..)을 처음 시작에 n에 담아놓고 반복때는 n에 담긴 값과 비교하면 되니 같은 값을 반환하는 메서드를 매번 호출한다면 이 관용구를 사용하면 된다.
메서드를 작게 유지하고 한가지 기능에 집중하는 것
지역 변수 범위를 최소화하는 마지막 방법은 메서드를 작게 유지하고 한 가지 기능에 집중하는 것이다.
한 메서드에서 여러가지 기능을 처리한다면 그중 한 기능과만 관련된 지역변수라도 다른 기능을 수행하는 코드에서 접근할 수 있다. 해결책은 단순히 메서드를 기능별로 쪼개면 된다.