지역변수의 범위를 최소화하자

2021-07-24

지역변수

지역변수를 생각 없이 선언하다 보면 변수가 쓰이는 범위보다 너무 앞서 선언하거나, 다 쓴 뒤에도 여전히 살아 있게 되기 쉽다.

지역변수의 범위는 선언된 지점부터 그 지점을 포함한 블록이 끝날 때까지이므로, 실제 사용하는 블록 바깥에 선언된 변수는 그 블록이 끝난 뒤 까지 살아 있게 된다.

그래서 실수로 의도한 범위 앞 혹은 뒤에서 그 변수를 사용하면 끔찍한 결과로 이어질 수 있다.

지역변수 선언과 초기화

거의 모든 지역변수는 선언과 동시에 초기화해야 한다. 초기화에 필요한 정보가 충분하지 않다면 충분해질 때까지 선언을 미뤄야 한다.

try-catch문은 이 규칙에서 예외다. 변수를 초기화하는 표현식에서 검사 예외를 던질 가능성이 있다면 try 블록안에서 초기화 해야 한다.

한편, 변수 값을 try 블록 바깥에서도 사용한다면 (비록 정확히 초기화하진 못하더라도) try 블록 앞에 선언해야 한다.

반복문의 지역변수

반복문은 독특한 방식으로 변수 범위를 최소화해준다.

예전의 for 형태든 새로운 for-each 형태든, 반복문에서 반복 변수의 범위가 반복문의 몸체, 그리고 for 키워드와 몸체 사이의 괄호 안으로 제한 된다.

따라서 반복 변수의 값을 반복문이 종료된 뒤에도 써야 하는 상황이 아니라면 while 문보다는 for문을 쓰는 편이 낫다.

for (Element e : c) {

}

반복자를 사용해야 하는 상황이면 for-each 문 대신 전통적인 for 문을 쓰는 것이 낫다

for (Iterator<Element> i = c.iterator(); i.hasNext();) {
  Element e = i.next();
}

while 문

Iterator<Element> i = c.iterator();
while (i.hasNext()) {
  doSomething(i.next());
}

Iterator<Element> i2 = c2.iterator();
while (i.hasNext()) { //버그
  doSomething(i2.next());
}

두 번째 while 문에는 복붙을 해서 오류가 생겼다.

새로운 반복 변수 i2를 초기화 했지만, 실수로 이전 while 문에서 쓴 i를 다시 쓴 것이다.

**불행히도 i의 유효 범위는 아직 끝나지 않았으므로, 이 코드는 컴파일도 잘 되고 실행 시 예외를 던지지 않는다. **

하지만 두 번째 while 문은 c2를 순회하지 않고 곧장 끝나버려 c2가 비었다고 착각하게 만든다.

for문을 사용하면 오류를 컴파일 타임에 잡아 준다.

for (Iterator<Element> i = c.iterator(); i.hasNext(); ) {
  Element e = i.next();
  // e와 i로 무언갈 한다.
}

//i 를 찾을 수 없다는 컴파일 오류를 낸다.
for (Iterator<Element> i2 = c2.iterator(); i.hasNext(); ) {
  Element e2 = i2.next();
  // e2와 i2로 무언갈 한다.
}

for 문의 장점은 다음과 같다.

  • 복붙 오류를 줄여 준다.
  • 변수 유효 범위가 for 문 범위와 일치하여 똑같은 이름의 변수를 여러 반복문에서 써도 서로 아무런 영향을 주지 않는다.
  • while문 보다 가독성이 좋다.

for문 캐싱

for (int i = 0, n = expensiveComputation(); i < n; i++) {
  
}

이 코드는 반복 여부를 결정 짓는 변수 i의 한곗값을 변수 n에 저장하여, 반복 때마다 다시 계산해야 하는 비용을 없앴다.

같은 값을 반환하는 메서드를 매번 호출한다면 이 관용구를 사용하면 좋다.

마무리

지역변수 범위를 최소화하는 마지막 방법은 메서드를 작게 유지하고 한가지 기능에 집중하는 것이다.

한 메서드에서 여러 가지 기능을 처리한다면 그중 한 기능과만 간련된 지역변수라도 다른 기능을 수행하는 코드에 접근할 수 있을 것이다.

해결책은 단순히 메서드를 기능별로 쪼개면 된다.