Starbucks Caramel Frappuccino
본문 바로가기
  • 그래 그렇게 조금씩
JAVA/Java Start

6. 스코프(Scope), 타입캐스팅(Type Casting)_형변환

by Toughie 2024. 1. 21.

변수는 기본적으로 선언 위치에 따라 전역 변수, 지역 변수, 클래스 변수(클래스 변수, 인스턴스 변수) 등으로 분류된다.

지금까지 학습하면서 함수 내부에서 사용한 변수들은 로컬 변수(Local Variable)로 코드 블럭을 벗어나면 사용이 불가능했다.

public class Scope1 {

    public static void main(String[] args) {
        int mainVariable = 10;

        if (true) {
            int ifVariable = 20;
            // x는 조건문 코드블럭 내부에서만 사용 가능
            System.out.println("ifVariable = " + ifVariable);
            // m은 main 메서드 코드블럭 내부에서만 사용 가능
            System.out.println("mainVariable = " + mainVariable);
        }
        // java: cannot find symbol symbol: variable x location: class scope.Scope1
//        System.out.println(x);
    } // mainVariable 생존 종료
}
public class Scope2 {

    public static void main(String[] args) {

        int mainVar = 10;

        for (int i = 0; i < 2; i++ ) {
            System.out.println("mainVar = " + mainVar); //블록 내부에서 외부는 접근 가능함
            System.out.println(i); //i는 for문 안에서만 접근 가능
        } // i 생존 종료
    }
}

Scope의 존재 의의는?

필요한 곳에서, 필요한 만큼만 사용하자!

 

1. 효율적인 메모리 사용

-> 사용이 끝나면 바로바로 메모리에서 해제되도록 메모리 누수를 방지하자.

 

2. 코드 단순화(복잡성 줄이기)

-> 특정 코드블럭에서만 사용되는 변수라면, 해당 코드 블럭 실행이 끝난 이후에는 신경을 쓰지 않아도 된다.

 

(cf. 전역 변수와 같이 아주 넓은 범위의 변수의 경우 접근 가능성이 높아서, 유지보수 시 고려해야 되는 부분이 늘어날 수 있다.)

(사이드 이펙트 가능성 증가)

    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);
    }
    // (스코프 측면에서) 더 효율적인 코드
       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);
    }

 

temp변수의 경우 조건문 내부에서 출력에 사용되고 나면 필요가 없기 때문에,

더 넓은 스코프(main 메서드 영역)에서 선언하는 것보다 조건문 내부에서 선언하는 것이 더 효율적이라고 볼 수 있다.

 

    public static void main(String[] args) {

        int sum = 0;
        int endNum = 10;
		
        //반복문이 끝나면 i에 접근할 수 없다.
        for (int i = 1; i <= endNum; i++) {
            sum += i;
            System.out.println("i = "+ i + ", sum = " + sum);
        }
    }

 

for loop에서 초기 변수 i를 위와 같이 작성할 수 있기 때문에, 반복문 내부에서만 사용되는 변수의 스코프를 좁힐 수 있다.

cf. while의 경우 더 넓은 스코프에 선언해줘야 한다.



Type Casting (형변환)

간단하게는 타입을 변경하는 것을 말한다.

 

기본적으로 작은 범위에서 큰 범위로 변환은 당연히 가능하다.

- int -> long -> double

 

하지만 큰 범위의 값을 작은 범위의 타입으로 바꾸려면 문제가 발생할 수 있다. (우겨넣기 느낌)

- 소수점 절삭, 오버플로우 등

    public static void main(String[] args) {
        int intValue = 10;
        long longValue;
        double doubleValue;


        longValue = intValue; // int -> long
        System.out.println("longValue = " + longValue);

        doubleValue = intValue; // int -> double
        System.out.println("doubleVablue = " + doubleValue);

        doubleValue = 30L; // long -> double
    }

 

기본적으로 정적 언어에서는 변수 타입과 동일한 값만 할당이 가능하다.

하지만 편의상 자동 형변환이 일어나는 것이다.

 

자동, 암시적, 묵시적 형변환의 과정 (implicit)

intValue = 10

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

(long), (double) 등이 생략되었다고 보면 된다.


명시적 형변환 (explicit)

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

- 소수점의 값에서 정수만 출력하는 경우 (int)를 명시적으로 작성하는 등

 

    public static void main(String[] args) {
        double doubleValue = 7.7;
        int intValue;

        // java: incompatible types: possible lossy conversion from double to int
//        intValue = doubleValue;

        // explicit
        intValue = (int) doubleValue;
        System.out.println("intValue = " + intValue);
    }

변환 과정에서 데이터 손실이 일어날 수 있다고 아주 친절하게 컴파일 에러를 내준다. 진짜 친절함..ㅋㅋ

 

명시적 형변환 과정

double doubleValue = 1.5;
intValue = (int) doubleValue;
intValue = (int) 1.5; //doubleValue에 있는 값을 읽는다.(복사)
intValue = 1; // (int)로 형변환. intValue에서 정수 부분인 숫자 1을 할당한다.
    public static void main(String[] args) {

        long maxIntValue = 2147483647; // 정수의 최대값, (long) 생략됨
        long maxIntOver = 2147483648L; // L을 붙이지 않으면 리터럴 값으로 int이기 때문에 컴파일 에러 발생
        int intValue = 0;

        intValue = (int) maxIntValue;
        System.out.println("intValue = " + intValue); //int 값 이내이기 때문에 문제가 없다.
        
        intValue = (int) maxIntOver;
        System.out.println("intValue = " + intValue); //-2147483648.. 오버플로우 발생
    }

 

오버플로우

2147483648은 int의 최댓값을 넘기 때문에 오버플로우가 발생한다.

-> int의 최솟값인 -2147483648로 출력되는데 (최대) -> (최소)로 순환되는 느낌으로 이해하면 된다.

 

(int) 2147483649은 -2147483647이 될 것이다.

 

오버플로우가 발생하지 않도록 넉넉한 상위 타입으로 변경하자. (long, double)

오버플로우로 계산하려고 하는 수상한 개발자가 꼭 있을 거 같은 예감.. ㅋㅋ 그러지 맙시다.


계산과 타입캐스팅

1. 같은 타입끼리의 계산은 같은 타입의 결과를 낸다.

ex. int + int 는 int, double + double은 double

 

2. 서로 다른 타입의 계산은 큰 범위로 자동 형변환이 일어난다.

int + long은 long, int + double은 double

    public static void main(String[] args) {
        int div1 = 3 / 2; // 계산결과가 int이기 때문에 1이 출력된다.
        System.out.println("div1 = " + div1);
    
        double div2 = 3 / 2; //계산 결과가 int이고, double로 자동형변환 되어 1.0이 나온다.
        System.out.println("div2 = " + div2); //
        
        double div3 = 3.0 / 2 ; // double과 int의 계산으로 double이 되어 1.5가 나온다.
        System.out.println("div3 = " + div3);

        double div4 = (double) 3 / 2; // 명시적 타입캐스팅을 통해 double과 int연산으로 double 결과값이 나온다(1.5)
        System.out.println("div4 = " + div4);

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

 

 

학습 출처

김영한의 자바 입문 - 코드로 시작하는 자바 첫걸음