본문 바로가기

Reading/Effective Java

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

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

 

지역변수의 범위를 줄이는 기법


1. 가장 처음 쓰일 때 선언하기

선언 시점부터 그 지점을 포함한 블록이 끝날때까지의 범위를 가지는 지역변수를 막 쓴다면 끔찍한 결과를 초래하니 처음 쓰일 때 선언하자.

  • 변수가 쓰이는 범위보다 앞서 선언될 경우,코드가 어수선하고 가독성이 떨어지며 사용 시점엔 타입과 초기값을 잊을 수 있다.
  • 실제 사용하는 블록 바깥에 선언된 변수는 그 블록이 끝나도 살아 있게 된다.

2. 거의 모든 지역변수는 선언과 동시에 초기화하기

  • 초기화에 필요한 정보가 충분하지 않다면 충분할 때까지 선언을 미루자.
  • try-catch문은 예외다. 

try-catch문은 변수를 초기화하는 표현식에서 검사 예외를 던질 가능성이 있다면 try 블록 안에서 초기화해야 한다. 그렇지 않으면 예외가 블록을 넘어 메소드에까지 전파된다. 하지만 변수 값을 try 블록 밖에서 사용해야할 수도 있다. 그렇게 사용해야할 경우 비록 정확히 초기화하진 못하더라도 try 블록 앞에서 선언해야 한다.

 

3. 적절한 반복문 선택하기

for문은 독특한 방식으로 변수 범위를 최소화 해준다. 반복 변수의 범위가 반복문의 몸체와 for 내부 괄호로 제한되기 때문이다.

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

 

[1] 반복자 사용 상황에서 유연하다.

 

List<Integer> lists = IntStream.range(0,3).boxed().collect(Collectors.toList());
// forEach - 순회에는 좋으나 반복자를 사용해야한다면? .next() .hasNext() .remove() 등
for (Integer list : lists) { }
// for - 다양한 상황에 유연
for(Iterator<Integer> i = lists.iterator();i.hasNext();){
    Integer next = i.next();
    ...
}

 

[2] 반복 변수를 사용할 때 실수를 컴파일 타임에 알 수 있다.

 

for(int i=0;i<2;i++);
// 조건문에 j 대신 i를 썼으나 컴파일 타임에 바로 잡힘
for(int j=0;i<5;j++);

 

위 코드는 i의 범위가 for문 내부기 때문에 컴파일 타임에 i를 잘못사용했다고 알 수 있다.

 

int k=0;
while(k<lists.size()){
    if(lists.get(k)==0)
        break;
    k++;
}
int k2=0;
// k2를 사용해야하는데 k를 사용해버리기
while(k<lists.size()){
    if(lists.get(k2)==0){
        break;
    }
    k2++;
}

 

 

while문의 경우 반복 변수를 미리 선언해야하고, 과정이 비슷해서 복사하다가 두 번째 while문 조건식에 k를 실수로 안고칠 수 있고, 컴파일 오류에도 나타나지 않고 예외도 없으며 우연찮게 잘 돌아가서 확인을 못한다면 한참 뒤에야 알 수 있다.

 

 

[3] 똑같은 이름의 변수를 여러 반복문에서 써도 서로 아무런 영향을 주지 않는다.

 

public static void main(String[] args) {
    for(int i=0;i<3;i++);
    for(int i=2;i<5;i++);
    for(int i=4;i<7;i++);
}

 

[4] 반복 때마다 다시 계산해야하는 비용을 없앨 수 있다.

 

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

 

변수 i의 한계값을 변수 n에 저장해서 반복 때마다 다시 계산해야하는 비용이 없다. 한계 값을 조건문에서 확인한다면 매번 expensive..() 메소드를 호출할 것이고, 그렇다고 외부 블럭에 두면 다 사용하고 나서 소멸하지 않고 그대로 존재한다는 단점이 있는데 그럴 필요가 없다.

 

 

 

4. 메소드를 작게 유지하고 한 가지 기능에 집중하기

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

이 경우 단순히 메소드를 기능별로 쪼개면 된다.

 

 

* 위 글은 EffectiveJava 3/E 책 내용을 정리한 글로, 저작권 관련 문제가 있다면 댓글로 남겨주시면 즉각 삭제조치 하겠습니다.