본문 바로가기

개발/이펙티브 자바

Effective Java ( 이펙티브 자바 ) - 9장 - 아이템 57

일반적인 프로그래밍 원칙 - 9장


 

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


지역변수의 유효 범위를 최소로 줄이면 코드 가독성과 유지보수성이 높아지고 오류 가능성은 낮아진다.

 

지역변수의 범위를 줄이는 가장 강력한 기법은 역시 '가장 처음 쓰일 때 선언하기'다.

지역변수를 생각 없이 선언하다 보면 변수가 쓰이는 범위보다 너무 앞서 선언하거나, 다 쓴 뒤에도 여전히 살아 있게 되기 쉽다. 지역변수의 범위는 선언된 지점부터 그 지점을 포함한 블록이 끝날 때까지임을 명심하자.

 

거의 모든 지역변수는 선언과 동시에 초기화해야 한다.

초기화에 필요한 정보가 충분하지 않다면 충분해질 때까지 선언을 미뤄야 한다. try-catch문은 이 규칙에서 예외다. 변수 초기화 표현식에서 검사 예외를 던질 가능성이 있다면 try 블록 안에서 초기화를 해야하고, 변수 값을 try 블록 바깥에서도 사용해야 한다면 try 블록 앞에서 선언해야 한다.

 

반복문은 독특한 방식으로 변수 범위를 최소화해준다. for 형태는 for, for-each든 반복 변수의 범위가 반복문의 몸체, 그리고 for 키워드와 몸체 사이의 괄호 안으로 제한된다. 따라서 반복문의 변수의 값을 반복문이 종료된 뒤에도 써야 하는 상황이 아니라면 while문보다는 for 문을 쓰는 편이 낫다.

 

예를 들어 다음은 컬렉션을 순회할 때 권장하는 관용구다.

for (Element e : c) {
   ... // e로 무언가를 한다.
}

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

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

다음의 두 while을 보면 for문이 더 나은 이유를 알 수 있다. 다음 코드에는 버그도 하나 숨어 있다.

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

...

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

  두 번째 while문에서는 실수로 앞의 i를 다시 썼다. i의 유효범위는 아직 끝나지 않았으므로, 컴파일도 잘 되고 실행 시 예외도 던지지 않는다. 하지만 두 번째 while문은 c2를 순회하지 않고 곧장 끝나버려 c2가 비었다고 착각하게 만든다. 

 

  for(for-each)문을 사용하면 이런 복사해 붙여넣기 오류를 컴파일 타임에 잡아준다. 

 

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

..

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

  for 문이 복사해 붙여넣기 오류를 줄여주는 이유는 또 있다. 변수 유효 범위가 for문 범위와 일치하여 똑같은 이름의 변수를 여러 반복문에서 써도 서로 아무런 영향을 주지 않는다. 

 

  장점이 하나 더 있다. while 문보다 짧아서 가독성이 좋다. 

 

  다음은 지역변수의 범위를 최소화하는 또 다른 반복문 관용구다.

for (int i = 0, n = expensiveComputation(); i < n; i++) {
   ... // i로 무언가를 한다.
}

i, n은 범위가 정확히 일치한다. 같은 값은 반환하는 메서드를 매번 호출한다면 이 관용구를 사용하기 바란다. 

 

지역변수의 범위를 최소화하는 마지막 방법은

메서드를 작게 유지하고 한 가지 기능에 집중하는 것이다. 단순히 메서드를 기능별로 쪼개면 된다.