5. 스코프, 형변환

스코프 1 - 지역 변수와 스코프

변수는 선언한 위치에 따라 지역변수, 맴버 변수(클래스, 인스턴스 변수)와 같이 분류 됨

package scope;
 
public class Scope1 {
     public static void main(String[] args) {
 
		int m = 10; //m 생존 시작 if (true) {
 
		int x = 20; //x 생존 시작
		System.out.println("if m = " + m); //블록 내부에서 블록 외부는 접근 가능 System.out.println("if x = " + x);
 
		} //x 생존 종료
 
		//System.out.println("main x = " + x); //오류, 변수 x에 접근 불가
		System.out.println("main m = " + m); } //m 생존 종료
}

int m

  • int m은 main 내부에서 선언되었으므로 main 블록 종료시점까지 생존한다.

int x

  • int x는 if 블록 내부에 선언되었으므로 if 블록 종료시점까지 생존한다

즉,

Scope란 변수의 접근 가능한 범위를 말한다.

스코프 2 - 존재 이유

다음 코드를 보자

 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);
     }
 
}

이 코드에서 temp는 if 조건 내부에서만 사용하는 코드이지만 main 블록에서 선언되어있다. 이러한 코드는 다음과 같은 문제가 발생한다.

  • 비효율적 메모리 사용
  • 코드 복잡성 증가
package scope;
 
 public class Scope3_2 {
     public static void main(String[] args) {
 
         int m = 10;
         if (m > 0) {
 
             int temp = m * 2;
 
             System.out.println("temp = " + temp);
         }
 
         System.out.println("m = " + m);
     }
 
}

다음과 같이 수정하면 유지보수하기 용이하다.

정리

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

형변환 - 자동 형변환

형변환

  • 작은 범위에서 큰범위로는 값을 넣을 수 있다.
    • int -> long -> double
  • 큰 범위에서 작은 범위는 다음과 같은 문제가 발생할 수 있다.
    • 소수점 버림
    • 오버플로우

작은 범위에서 큰 범위로 대입은 허용한다.

package casting;
 public class Casting1 {
 
     public static void main(String[] args) {
         int intValue = 10;
         long longValue;
         double doubleValue;
 
         longValue = intValue; // int -> long
         System.out.println("longValue = " + longValue);
         //longValue = 10
 
         doubleValue = intValue; // int -> double
 
         System.out.println("doubleValue1 = " + doubleValue);
         //doubleValue1 = 10.0
 
         doubleValue = 20L; // long -> double
 
		 System.out.println("doubleValue2 = " + doubleValue);
		 //doubleValue2 =20.0
 
	}
}
 
 
//결과
longValue = 10
doubleValue1 = 10.0
doubleValue2 = 20.0
 
  • 자바는 기본적으로 같은 타입에 값을 대입할 수 있다.
  • int < long 이다. 따라서 long -> int는 대입 가능하다.

자동 형변환

하지만 결국 데이터 타입을 맞춰야 하기 때문에 개념적으로는 다음과 같이 동작한다.

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

이렇게 형을 변경하는 것을 형변환이라고 한다.

작은 범위 숫자 타입에서 큰 범위 숫자 타입으로의 대입은 개발자가이렇게 직접 형변환 하지 않아도 된다. 이런 과정이 자동으로 일어나기 때문에 자동 형변환, 또는 묵시적 형변환이라 한다.

형변환 - 명시적 형변환

큰 범위에서 작은 범위 대입은 명시적 형변환이 필요하다.

package casting;
 
public class Casting2 {
    public static void main(String[] args) {
        double doubleValue = 1.5;
        int intValue = 0;
		//intValue = doubleValue;
		//컴파일 오류 발생 intValue = (int) doubleValue;
		//형변환 System.out.println(intValue);
		//출력:1
    }
}

이런 컴파일에러는 개발자가 타입을 통해 안전하게 개발 가능하게 한다.

하지만 만약 개발자가 위험을 감수하고도 값을 대입하고 싶다면 데이터 타입을 강제로 변경할 수 있다.

이땐 ()를 사용해 명시적으로 입력하면 된다.

intValue = (int) doubleValue; //형변환

이것을 타입을 바꾼다해서 형 변환(명시적 형변환)이라고 한다. 영어로는 캐스팅이라 한다.

명시적 형변환 과정

//doubleValue = 1.5
intValue = (int) doubleValue;
intValue = (int) 1.5; //doubleValue에 있는 값을 읽는다.
intValue = 1; //(int)로 형변환 한다. intValue에 int형인 숫자 1을 대입한다.

형 변환을 한다고 해서 doubleValue 자체의 타입이나 내부 값이 변경되는 것은 아니다.

🖋️

참고

변수의 값은 대입 연산자(=)를 통해서 직접 대입할 때만 변경된다.

형변환과 오버플로우

형변환을 할때 만약 작은 숫자가 표현할 수 있는 범위를 넘어서면 어떻게 될까?

package casting;
public class Casting3 {
    public static void main(String[] args) {
        long maxIntValue = 2147483647; //int 최고값
        long maxIntOver = 2147483648L;
        //int 최고값 + 1(초과) int intValue = 0;
 
        intValue = (int) maxIntValue; //형변환
        System.out.println("maxIntValue casting=" + intValue);
        //출력:2147483647
 
        intValue = (int) maxIntOver; //형변환
        System.out.println("maxIntOver casting=" + intValue);
        //출력:-2147483648 }
    }
}
 
 
// 결과
 
 maxIntValue casting=2147483647
 maxIntOver  casting=-2147483648

정상범위

maxIntValue를 보면 int 타입의 가장 높은 숫자를 입력했다. 이 경우 int 범위 밖에 포함되기 때문에 long -> int로 형변환을 해도 아무런 문제가 없다.

초과 범위

다음으로 maxIntOver를 보면 int 타입의 가장 높은 숫자 + 1을 입력했다. 이 숫자는 리터럴은 int 범위를 벗어나기 때문에 형변환시 문제가 발생한다.

계산과 형변환

형변환은 대입 뿐 아니라, 계산시에도 발생한다.

 
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
    }
}
 
 
// 출력
 
div1 = 1
div2 = 1.0
div3 = 1.5
div4 = 1.5
result = 1.5

자바의 계산은 다음 2가지를 기억하자.

  • 같은 타입끼리의 계산은 같은 타입의 결과를 반환한다.
  • 서로 다른 타입의 계산은 큰 범위의 타입으로 자동 형변환이 일어난다.

정리

  • 작은 범위에서 큰범위로는 대입할 수 있다.
    • 이를 묵시적 형변환 또는 자동 형변환이라 한다.
  • 큰 범위에서 작은 범위의 대입은 다음과 같은 문제가 발생할 수 있다. 이때는 명시적 형변환을 사용해 방지할 수 있다.
    • 소수점 버림
    • 오버플로우
  • 연산과 형변환
    • 같은 타입의 연산은 같은 타입을 반환한다.
    • 다른 타입의 연산은 큰 범위의 타입으로 자동 형변환이 일어난다.