개발/Effective Java

[Effective Java] item 57. 지역변수의 범위를 최소화하라

nova_dev 2021. 3. 14. 00:00
반응형

[Effective Java] item 57. 지역변수의 범위를 최소화하라

지역변수의 유효범위

지역변수의 유효범위를 줄이면 어떤것이 좋을까?

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

지역변수의 유효범위를 어떻게 줄일까?

1. 가장 처음 쓰일 때 선언하자
  • 미리 선언해두면 코드가 어수선해져 가독성이 떨어진다. 변수를 실제로 사용하는 시점엔 타입과 초깃값이 기억나지 않을 수 있다.
  • 지역변수를 생각 없이 선언하다 보면 변수가 쓰이는 범위보다 너무 앞서 선언한다거나, 다 쓴 뒤에도 여전히 살아 있게 되기 쉽다.
2. 선언과 동시에 초기화하자.
  • 초기화에 필요한 정보가 충분하지 않다면 충분해질 때까지 선언을 미뤄야 한다.
  • 단, try-catch 문은 이 규칙에서 예외다. 변수를 초기화하는 표현식에서 검사 예외를 던질 가능성이 있다면 try 블록 안에서 초기화해야 한다.
3. 메서드를 작게 유지하고 한 가지 기능에 집중하자
  • 한 메서드에서 여러 가지 기능을 처리한다면 그중 한 기능과만 관련된 지역변수라도 다른 기능을 수행하는 코드에서 접근할 수 있을 것이다.
  • 해결책은 간단하다. 단순히 메서드를 기능별로 쪼개면 된다.

반복문과 지역변수

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

컬렉션이나 배열을 순회하는 권장 관용구
for (Element e : c) {
    ... // e로 무언가를 한다.
}

위 예시는 컬렉션을 순회할때 권장하는 관용구이다.

반복자가 필요할 때의 관용구
for (Iterator<Element> i = c.iterator(); i.hasNext(); ){
    Element e = i.next();
    ... // e와 i로 무언가를 한다.
}

위 예시와 같이 반복자를 사용해야 하는 상황이면 (반복자의 remove 메서드를 써야 한다든가) for-each 문 대신 전통적인 for문을 쓰는 것이 낫다.

for문이 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-each를 포함한) for문을 사용하려면 이런 복사해 붙여넣기 오류를 컴파일타임에 잡아준다. 첫 번째 반복문이 사용한 원소와 반복자의 유효 범위가 반복문 종료와 함께 끝나기 때문이다. 다음은 전통적인 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 문 범위와 일치하여 똑같은 이름의 변수를 여러 반복문에서 써도 서로 아무런 영향을 주지 않는다. 사실 이렇게 쓰는 게 더 세련되기까지 하다.

for문의 장점을 하나만 더 이야기 해보자면, 바로 while 문보다 짧아서 가독성이 좋다는 점이다.

지역변수의 범위를 최소화하는 또 다른 반복문 관용구
for (int i = 0; n = expensiveComputation(); i < n; i++) {
    ... // i로 무언가를 한다.
}

이 관용구에서 주목할 부분은 범위가 정확히 일치하는 두 반복 변수 i와 n이다. 반복 여부를 결정짓는 변수 i의 한곗값을 변수 n에 저장하여, 반복 때마다 다시 계산해야 하는 비용을 없앴다. 같은 값을 반환하는 메서드를 매번 호출한다면 이 관용구를 사용하기를 바란다.

반응형