Java

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

일태우 2021. 1. 5. 21:23

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

제목의 내용은 '클래스와 멤버의 접근 권한을 최소화하라'와 비슷하다. 지역변수의 유효 범위를 최소로 줄이면 코드 가독성과 유지보수성이 높아지고
오류 가능성은 낮아진다.

C와 같이 역사가 깊은 프로그래밍 언어 중에는 지역변수를 코드 블록의 첫머리에 선언하는 경우가 많고, 이 방식을 습관처럼 따르는 프로그래머도 있다.
하지만 자바에서는 문장을 선언할 수 있는 곳이면 어디서든지 선언할 수 있다.(C도 C99 표준부터는 변경되었다.)

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

그리고 거의 모든 지역변수는 선언과 동시에 초기화해야 한다. 초기화에 필요한 정보가 충분치 않다면, 충분해질 때 까지 선언을 미뤄야 한다. 하지만
try-catch문은 이 규칙에서 예외다. 변수를 초기화 하는 표현에서 검사 예외를 던질 가능성이 있다면 try 블록 안에서 초기화 해야한다.(그렇지 않으면 예외가 블록을 넘어 메서드에 전파된다.)

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

반복문은 독특한 방식으로 변수 범위를 최소화 한다. 예전의 for문이든 for-each 형태든, 반복문에서는 반복 변수의 범위가 반복문 블럭과 for 키워드로 제한된다.
따라서 반복 변수의 값을 반복문이 종료된 뒤에도 써야 하는 상황이 아니라면 while 문보다는 for문이 낫다.

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

// 컬렉션이나 배열을 순회하는 권장 관용구
for (Element e : c) {
    ... // do something with e
}

반복자를 사용해야 하는 상황이면 for-each보다 for문이 낫다.

for (Iterator<Element> i = c.iterator(); i.hasNext(); ) {
    Element e = i.next();
    ... // do something with e and 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 (Iterator<Element> i = c.iterator(); i.hasNext(); ) {
    doSomething(i.next());
}
...
// 다음은 "i를 찾을 수 없다"는 컴파일 오류를 낸다.
for (Iterator<Element> i2 = c2.iterator(); i.hasNext(); ) {
    doSomethingElse(i2.next());
}

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

for (int i = 0, n = expensiveComputation(); i < n; i++) {
    // do something with i
}

이 관용구에서는 두 반복 변수 i와 n이 핵심이다. 변수 i의 한계값을 변수 n에 저장하여, 반복 할 때마다 다시 계산해야하는 비용을 없앴다.
같은 값을 반환하는 메서드를 매번 호출한다면 이 관용구를 사용하기 바란다.

지역변수의 범위를 최소화하는 마지막 방법은 메서드를 작게 유지하고 한 가지 기능에 집중하는 것이다(단일책임원칙) 한 메서드에서 여러가지 기능을 처리한다면
그중 한 기능과만 관련된 지역변수라도 다른 기능을 수행하는 코드에서 접근할 수 있을 것이다. 해결책은 단순히 기능별로 메서드를 쪼개면 된다.

 

출처: effective java item57