Updated:

스코프

지역 변수와 스코프

 변수는 선언한 위치에 따라 지역 변수, 맴버 변수(클래스 변수, 인스턴스 변수)와 같이 분류된다.
 지역 변수는 이름 그대로 특정 지역에서만 사용할 수 있는 변수라는 뜻이다. 그 특정 지역을 벗어나면 사용할 수 없다. 여기서 말하는 지역이 바로 변수가 선언된 코드 블록 {}이다. 지역 변수는 자신이 선언된 코드 블록안에서만 생존하고, 자신이 선언한 코드 블록을 벗어나가면 제거된다. 따라서 이후에는 접근할 수 없다.
 변수의 접근 가능한 범위를 스코프(Scope)라 한다. 참고로 Scope를 번역하면 범위라는 뜻이다.

스코프 존재 이유

 변수를 선언한 시점부터 변수를 계속 사용할 수 있게 해도 되지 않을까? 왜 복잡하게 접근 범위(스코프)라는 개념을 만들었을 까? 이해를 위해 다음 코드를 보자.

package scope;

public class Scope3_1 {

    public static void main(String[] args) {
        int m = 10;
        int temp = 0;
        if (m > 0) {
            temp = m * 2;
            System.out.println("temp = " + temp);
        }
        System.out.println("m = " + m);
    }
}

 조건이 맞으면 변수 m의 값을 2배 증가해서 출력하는 코드이다. 여기서 temp를 사용했는데 좋은 코드라고 보기는 어렵다. 왜냐하면 임시 변수 temp는 if 조건이 만족할 때 임시로 잠깐 사용하는 변수이다. 그런데 임시 변수 temp를 main() 코드 블록에서 선언되어 있다. 이렇게 되면 다음과 같은 문제가 발생한다.

  • 비효율적인 메모리 사용
    • temp는 if코드 블록에서만 필요하지만, main() 코드 블록이 종료될 때 까지 메모리에 유지된다. 따라서 불필요한 메모리가 낭비된다.
  • 코드 복잡성 증가
    • 좋은 코드는 군더더기 없는 단순한 코드이다. 위 코드는 스코프가 불필요하게 넓은 것이다.

 정리하면, 변수는 꼭 필요한 범위로 한정해서 사용하는 것이 좋다. 변수의 스코프는 꼭 필요한 곳으로 한정해서 사용한다. 메모리를 효율적으로 사용하고 더 유지보수하기 좋은 코드를 만들 수 있다. 또, 좋은 프로그램은 무한한 자유가 있는 프로그램이 아니라 적절한 제약이 있는 프로그램이다.

형변환

자동 현변환

 작은 범위에서 큰 범위로는 당연히 값을 넣을 수 있다 (ex. int -> long -> double). 큰 범위에서 작은 범위는 소수점 버림, 오버프로우같은 문제가 발생할 수 있다.
 결국 대입하는 형(타입)을 맞추어야 하기 때문에 개념적으로는 다음과 같이 동작한다.

int intValue = 10;
double doubleValue;

doubleValue = intValue;
doubleValue = (double) intValue; // 형 맞추기
doubleValue = (double) 10; // 변수 값 읽기
doubleValue = 10.0; // 형변환

 이렇게 앞에 (double)과 같이 적어주면 int 형이 double 형으로 형이 변한다. 이렇게 형이 변경되는 것을 형변환이라 한다. 작은 범위 수자 타입에서 큰 범위 숫자 타입으로 대입은 개발자가 이렇게 직접 형변환을 하지 않아도 된다. 이런 과정이 자동으로 일어나기 때문에 자동 형변환, 또는 묵지석 형변환이라 한다.

명시적 형변환

 큰 범위에서 작은 범위로 대입은 명시적 형변환이 필요하다. 예를 들어, double은 실수를 표현할 수 있기 때문에 1.5가 가능하다. 그런데 int는 실수를 표현할 수 없다. 이 경우 double을 int에 대입하면 숫자가 손실되는 문제가 발생할 수 있다.
 이런 문제는 매우 큰 버그를 유발할 수 있다. 그래서 자바는 이런 경우 컴파일 오류를 발생시킨다.
 하지만 만약 이런 위험을 개발자가 감수하고도 값을 대입하고 싶다면 데이터 타입을 강제로 변경할 수 있다. 형변환은 다음과 같이 변경하고 싶은 데이터 타입을 (int)와 같이 괄호를 사용해서 명시적으로 입력하면 된다(ex, intValue = (int) doubleValue;)
 이것을 형(타입)을 바꾼다고 해서 형변환이라 한다. 영어로는 캐스팅(casting)이라 한다. 개발자가 직접 형변환 코드를 입력한다고 해서 명시적 형변환이라 한다.

형변환과 오버플로우

 형변환을 할 때 만약 작은 숫자가 표현할 수 있는 범위를 넘어서면, 예를 들어 int형에 int로 표현할 수 있는 가장 큰 숫자인 2,147,483,647보다 1큰 숫자를 입력하면 -2,147,483,647이라는 결과가 나온다. 보통 오버플로우가 발생하면 마치 시계가 한바퀴 돈 것 처럼 다시 처음부터 시작한다. 참고로 -2,147,483,648은 int의 가장 작은 숫자이다.
 중요한 것은 오버플로우가 발생하는 것 자체가 문제라는 점이다. 오버플로우가 발생했을 때 결과가 어떻게 되는지 계산하는게 시간을 낭비하면 안된다. 오버플로우 자체가 발생하지 않도록 막아야한다. 이 경우 단순히 대입하는 변수의 타입을 int에서 long으로 변경해서 사이즈를 늘리면 오버틀로우 문제가 해결된다.

계산과 형변환

 형변환은 대입 뿐만 아니라, 계산을 할 때도 발생한다.

package casting;

public class Casting4 {

    public static void main(String[] args) {
        int div1 = 3 / 2;
        System.out.println("div1 = " + div1);   // 1

        double div2 = 3 / 2;
        System.out.println("div2 = " + div2);   // 1.0

        double div3 = 3.0 / 2;
        System.out.println("div3 = " + div3);   // 1.5

        double div4 = (double) 3 / 2;
        System.out.println("div4 = " + div4);   // 1.5

        int a = 3;
        int b = 2;
        double result = (double) a / b;
        System.out.println("result = " + result);   // 1.5
    }
}

 자바에서 계산은 다음 2가지를 기억하면 된다.

  • 같은 타입끼리의 계산은 같은 타입의 결과를 낸다.
    • int + int는 int를, double + double은 double의 결과가 나온다.
  • 서로 다른 타입의 계산은 큰 범위로 자동 형변환이 일어난다.
    • int + long은 long + long으로 자동 형변환이 일어난다.
    • int + doubledms double + double로 자동 형변환이 일어난다.

 다음 예를 보자.

int div1 = 3 / 2; // int / int
int div1 = 1; // int, int이므로 int타입으로 결과가 나온다.

double div2 = 3 / 2;  // int / int
double div2 = 1;  // int, int이므로 int타입으로 결과가 나온다.
double div2 = (double) 1; // int -> double에 대입해야 한다. 자동 형변환 발생
double div2 = 1.0;  // 1(int) -> 1.0(double)로 형변환이 된었다.

double div3 = 3.0 / 2;  // double / int
double div3 = 3.0 / (double) 2; // double / double로 형변환이 발생한다.
double div3 = 3.0 / 2.0;  // double / double -> double이 된다.
double div3 = 1.5;

double div4 = (double) 3 / 2; // 명시적 형변환을 사용했다. (double) int / int
double div4 = (double) 3 / (double) 2; // double / double로 형변환이 발생한다.
double div4 = 3.0 / 2.0;  // double / double -> double이 된다.
double div4 = 1.5;

 3 / 2와 같이 int형끼리 나눗셈을 해서 소수까지 구하고 싶다면 div4의 예제처럼 명시적으로 형변환을 사용하면 된다. 물론 변수를 사용하는 경우에도 “double result = (double) a / b;”러럼 형변환을 할 수 있다.

댓글남기기