티스토리 뷰

Spring/Java

[Java] 유효범위

DevJunku 2021. 12. 2. 19:57

🧱 유효범위의 개념

변수와 메소드 같은 것은 사용할 수 있는 이름이 있기 때문에 아래의 코드에서 left는 변수이고 sum은 메소드이다.

class EffectRange {

    int left;
    public void sum() {};

}

참고로 위의 코드는 코틀린에서 다음과 같이 변형될 수 있음

class EffectRange {
        var left: Int
        fun sum() {}
}

보시다시피 알겠지만, 코틀린 코드가 더 깔끔하긴 함.

여튼 중요한 것은 프로그램의 범위가 커지면, 여러 가지 이유로 충돌이 발생한다. 이를 해결하기 위해서 고안된 것이 바로 유효범위 라는 것으로 흔히 scope라고 부른다. 아래의 예시로 확인해보자

public class ScopeDemo {

    static void a() {
        int i = 0;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++ ) {
            a();
            System.out.println(i);
        }
    }
}

/*
0
1
2
3
4
*/

a라는 메소드를 static으로 class 메소드를 만든 이후 변수로 선언해버렸다. 그래서 어디든지 접근할 수 있어야 하는 전역 변수인데, 출력값은 그렇지 않다. 왜냐하면 static field 메소드 안에서 생성된, 변수는 그 안에서만 작동 할 수 있기 때문이다. 중요한 개념이니 잊지말자!

🧱 다양한 유효범위들

디렉토리를 생각하면 쉽게 이해가 가능하다. 많은 파일들이 존재할 때 실제로 PC 내에서는 한 폴더 내에 같은 이름을 갖는 경우는 제외하려고 한다. 이유는 간단하다. 중복되어 혼란을 야기시킬 수 있기 때문에, 하지만, 폴더의 명칭이 다른 곳에서 같은 이름을 갖는 파일은 본적이 있을 것이다. 해당 부분은 문제가 되지 않는다. 즉, 운영체제 안에서 파일의 meta 데이터가 완전히 일치하는 경우가 아니라면, 같은 이름의 파일을 여러 개 생성할 수 있을 것이다.

다음의 예제를 생각해보자.

static void a() {
        int i = 5;
}

위의 변수 명 i는 a메소드 안에서만 동작하기 때문에, 이름의 충돌을 막을 수 있다. 하지만 만약에 다음과 같이 class를 파일을 작성한다면?

public class ScopeDemo {

    static int i;

    static void a() {
        i = 0;
    }

    public static void main(String[] args) {
        for (i = 0; i < 5; i++ ) {
            a();
            System.out.println(i);
        }
    }
}

이런 경우라면, 계속 0만 출력되는 문제가 발생하게 된다. 왜? for 문에서 i를 통제하지 않았기 때문에 그렇다.

그리고 만약에 다음과 같이 짠다면, 이것도 문제가 됨

public class ScopeDemo {

    static void a() {
        String title = "coding EveryBody"
    }

    public static void main(String[] args) {
        a();
        System.out.println(title); // 이 부분도 오류임
    }
}

또는 다음과 같이 작성해도 문제 됨

public class ScopeDemo {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            System.out.println(i);
        }
        System.out.println(i); // 이거 오류임 (존재하지 않는 변수를 출력하는 것이기 때문에..)
    }
}

이후의 코드임

public class ScopeDemo {
    static int i = 5;

    static void a() {
        int i = 10;
        b();
    }

    static void b() {
        System.out.println(i);
    }

    public static void main(String[] args) {
        a(); // 이건 static int i = 5; 가 출력된 것임 나머지는 모두 scope 내에서 끝
    }
}

즉, 메소드에서 변수를 참고 할 때, 메소드 스코프 내의 변수를 확인하고 없으면, static field에서 갖고온다. 그렇지 않은 메소드에서 선언된 같은 이름의 변수는 갖고오지 못한다.

지역 변수 > 전역 변수

class C {
    int v = 10;

    void m() {
        int v = 20;
        System.out.println(v);
    }
}

public class ScopeDemo {
    public static void main(String[] args) {
        C c = new C();
        c.m();
    }
}

위 코드에서는 20이 출력됨.

하지만 만약에 this를 추가시키면 다음과 같이 됨.

class C {
    int v = 10;

    void m() {
        int v = 20;
        System.out.println(this.v);
    }
}

public class ScopeDemo {
    public static void main(String[] args) {
        C c = new C();
        c.m();
    }
}
/*
10
*/

this는 전역변수를 가르키는 말임.

유효변수라는 건 지금까지 전역변수와 지역변수로 나눠서 변수를 관리하기 더 편하게끔 만들어 놓은 것을 의미한다. 그리고 객체라는 개념이 존재하지 않는 절차지향 프로그래밍에서는 모든 메소드에 접근이 가능한 변수의 사용을 죄악시 하는 경향이 있다. 하지만, 이렇게 지역변수와 전역변수를 나눔으로써 이러한 죄악시 되는 경향을 객체지향 프로그래밍에서는 생각하지 않아도 된다.

하지만 그렇다고 무조건적으로 전역변수를 늘리는 것은 옳지 않다 필요한 변수를 심히 고민하여 전역변수로서 활용할 가치가 충분할때 사용하는 것이 좋다. 왜냐하면, 전역변수를 무분별하게 늘리게 되면, 관리해야할 전역변수가 많아지고, 그만큼 해당 객체의 크기가 커지기 때문이다.