DEV ℧ Developer Diary

[EffectiveJava] item57 - 지역변수의 범위를 최소화하라

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

지역변수의 선언

C와 같은 역사가 깊은 프로그래밍 언어 중에는 지역변수를 코드블록의 첫머리에 선언하느 경우가 많다. 하지만 자바는 문장을 선언할 수 있는 곳이면 어디서든 변수를 선언 할 수 있다.

지역변수의 범위를 줄이는 가장 강력한 기법은 역시 ‘가장 처음 쓰일 때 선언하기’다. 미리 선언부터 하면 코드가 어수선해져 가독성이 떨어지거나, 실제로 사용하는 시점엔 타입과 초기값이 기억나지 않을 수 있다.

변수가 쓰이는 범위보다 너무 앞서 선언하거나, 다 쓴 뒤에도 여전히 살아있게 될 수있다. 실제 사용하는 블록 바깥에 선언된 변수는 그 블록이 끝날때까지 살아 있께 되므로, 실수로 의도한 범위 앞 혹은 뒤에서 그변수를 사용하면 의도치 않는 결과가 발생 할 수 있다.

지역변수의 초기화

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

try-catch 문은 이 규칙에서 에외다. 변수를 초기화 하는 표현식에서 검사 예외를 던질 가능성이 있다면, try 블록 안에서 초기화 해야한다. 한편 변수값을 try 블록 바깥에서도 사용해야 한다면 try 블록 앞에서 선언해야 한다.

반복문에서 지역변수

반복문은 독특한 방식으로 변수 범위를 최소화해준다. 예전의 for 형태든 새로운 for-each 형태든, 반복문에서는 반복 변수의 범위가 반복문의 몸체 그리고 for 키워드와 몸체 사이의 괄호 안으로 제한된다.

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

예를 들어 컬렉션을 순회할때 사용하는 코드를 살펴보자.

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

만약 반복자를 사용해야 하는 상황이라면 (반복자의 remove 메서드와 같은 것) for-each 문 대신 전통적인 for 문을 쓰는 것이 낫다.

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

while vs for

다음의 while 문을 보면 앞서의 for 문이 더 나은 이유를 알 수 있다.

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의 유효범위는 그대로 이므로, 컴파일도 잘되고 실행시 예외도 던지지 않는다. 다만 i2를 지나치는 결과가 발생할 것이다.

for-each나 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 (int i = 0, n = expensiveComputation(); i < n; i++) {
    ... /** i로 무언가를 한다. */
}

이 관용구에서 주목할 부분은 범위가 정확히 일치하는 두 반복변수 i, n이다. 반복 여부를 결정짓는 변수 i의 한계값을 변수 n에 저장하여, 반복 때마다 다시 계산하는 비용을 없앴다.

메서드 내부의 지역변수 범위 최소화

메서드를 작게 유지하고 한가지 기능에 집중하는 것이 중요하다. 한 메서드에서 여러가지 기능을 처리한다면 그중 한기능과만 관련된 지역변수라도 다른 기능을 수행하는 코드에서 접근할 수 있을 것이다.
해결법은 간단하다. 단순히 메서드를 기능별로 쪼개면 된다.