명시적 형변환과 암묵적 형변환

1. 자바의 자료형

형변환을 이해하기 위해서는 자바의 자료형(Data Type, 데이터 타입)을 이해해야 한다. 자료형은 다양한 갈래의 데이터를 구분하는 기준이다. 자료형은 데이터의 저장 방식에 따라 기본형과 참조형 두 가지로 나뉜다.

  • 기본형(Primitive Type): 변수의 메모리 공간에 값이 할당된다.

    • 논리형: boolean

    • 문자형: char

    • 정수형: byte, short, int, long

    • 실수형: float, double

  • 참조형(Reference Type): 변수의 메모리 공간에 값의 주소가 저장된다.

    • new 예약어로 생성한다.

잘 이해가 가지 않는다면 아래 예시와 함께 생각해보자.

class Reference {
    int v9 = 9;
}

public class Ex1_PrintDataType {
    public static void main(String[] args) {
        // Primitive Type
        boolean v1 = false;
        char v2 = '2';
        byte v3 = 3;
        short v4 = 4;
        int v5 = 5;
        long v6 = 6;
        float v7 = 7.0f;
        double v8 = 8.0;

        System.out.println("v1 = " + v1);
        System.out.println("v2 = " + v2);
        System.out.println("v3 = " + v3);
        System.out.println("v4 = " + v4);
        System.out.println("v5 = " + v5);
        System.out.println("v6 = " + v6);
        System.out.println("v7 = " + v7);
        System.out.println("v8 = " + v8);

        // Reference Type
        Reference reference = new Reference();

        System.out.println("reference = " + reference);
    }
}

Ex1_PrintDataType 클래스는 기본형과 참조형 데이터를 출력한다. 출력 결과는 어떻게 될까?

v1 = false
v2 = 2
v3 = 3
v4 = 4
v5 = 5
v6 = 6
v7 = 7.0
v8 = 8.0
reference = Reference@1e643faf

기본형인 v1~v8은 실제 값이 출력되었지만, 참조형인 reference는 주소값이 출력되었다. v9의 값을 출력하려면 어떻게 해야 할까?

System.out.println("reference.v9 = " + reference.v9);

참조 변수 reference에서 실제 값을 저장하고 있는 변수 v9까지 한번 더 접근해야 한다.

2. 자바의 형변환

자바의 자료형을 알았으니 형변환도 이해할 수 있다. 형변환이란 변환이라는 단어처럼, 변수의 자료형을 변경하는 것이다.

그냥 처음에 정의한 자료형을 쭉 쓰면 안 되나요? 형변환 꼭 해야 하나요?

사람 마음이 변하듯 변수에 저장되는 값도 상황에 따라 바뀐다. 형변환은 다양한 상황에 적절히 대응하기 위해 필요하다. 형변환은 변환 방향에 따라 암묵적 형변환과 명시적 형변환 두 가지로 나뉜다.

  • 명시적 형변환(Casting): 메모리 크기가 큰 타입을 작은 타입으로 변경하는 것

    • 반드시 형변환 기호를 붙여줘야 하므로 명시적 형변환이라고 함

  • 암묵적 형변환(Promotion): 메모리 크기가 작은 타입을 큰 타입으로 변경하는 것

    • 형변환 기호를 붙이지 않아도 캐스팅이 일어나서 자동 형변환이라고도 함

자료형에 따라 하위 타입과 상위 타입의 정의는 달라진다. 암묵적 형변환은 자동으로 이뤄지는 반면에 명시적 형변환은 직접 타입을 지정해야 한다. 개념은 다 배웠으니 실제 코드를 통해 자세히 알아보자.

2.1. 기본형 타입의 형변환

기본형 타입의 형변환은 어떻게 이루어질까?

public class Ex2_PrimitiveTypeCasting {
    public static void main(String[] args) {
        int v1 = true; // 에러
        int v2 = (int) true; // 에러
        int v3 = 1.0f; // 에러

        float v4 = 1; // 암묵적 형변환
        int v5 = (int)1.0f; // 명시적 형변환
    }
}

Ex2_PrimitiveTypeCasting 클래스는 기본형을 형변환하는 몇 가지 예시를 보여준다. 예제를 통해 형변환이 항상 성공하지는 않는다는 걸 알 수 있다. v1과 v2, int와 boolean 간의 형변환은 왜 실패했을까? int와 boolean는 기본 자료형이지만, 서로 저장하는 데이터의 종류가 달라 형변환에 실패했다. 다시 말해 형변환을 하려면 변경 전/후 자료형이 서로 관련 있어야 한다.

png

위 그림은 기본 자료형의 관계와 형변환 방향을 나타낸다. 화살표 방향을 따라서 형변환을 하면 암묵적 형변환, 반대로 형변환하면 명시적 형변환이다. 그렇다면 기본형의 형변환은 어떤 문제도 없을까? 물론 정답은 '아니오' 이다. 명시적 형변환의 경우 데이터 저장 공간이 줄어들어 데이터가 손실될 가능성이 있다.

2.2. 참조형 타입의 형변환

참조형 타입의 형변환은 어떻게 이루어질까? 기본형 타입에서 배운 것처럼 서로 관련이 있는 자료형만 형변환이 가능하다. 그렇다면 참조형 타입에서 관련이 있다는 건 어떤 의미일까?

  • 상속 관계를 맺고 있는 경우

  • 인터페이스로 확장해서 구현한 경우

위와 같은 상황이면 두 자료형이 관련이 있다고 볼 수 있다.

png

위와 같은 상속 관계를 가지는 Ipad, Mini 클래스가 있다고 가정하자.

class Ipad {
    int volume;
}

class Mini extends Ipad {
    void scrolling() {
        System.out.println("Scrolling.");
    }
}

public class Ex3_ReferenceTypeCasting {
    public static void main(String[] args) {
        Mini mini1 = new Ipad(); // 명시적 형변환(에러)
        Mini mini2 = (Mini) new Ipad(); // 명시적 형변환(에러)

        Ipad ipad = new Mini(); // 암묵적 형변환
        ipad.scrolling(); //  자료형 접근(에러)
    }
}

기본형과 마찬가지로 암묵적 형변환은 큰 무리 없이 동작한다. 그런데 명시적 형변환은 에러가 일어나고 있다. 기본형과 달리 형 변환 기호를 붙였음에도 에러가 일어나는 이유가 뭘까? 에러가 나는 이유는 JVM이 참조형 데이터를 추측할 수 없기 때문이다. 기본 자료형의 경우 이미 컴파일러가 어떤 자료형인지 알고 있으므로 변환이 가능하다. 그러나 참조형의 경우 무엇이 저장될지 알 수 없으므로 추측도 불가능하다.자료형과 함께 선언된 변수는 필요한 데이터를 누락없이 제공해야 한다. 참조형 변수의 명시적 형변환은 필연적으로 데이터 누락을 동반하므로 에러가 일어나는 것이다. 즉 원칙적으로는 명시적 형변환이 불가능하다. 다만 암묵적 형변환도 에러가 일어나지 않았을 뿐 실제로 Ipad의 인스턴스 ipad가 Mini의 인스턴스가 되지 않는다. 따라서 ipad 참조 변수가 Mini 클래스의 속성과 메서드에 접근하면 에러가 출력된다.

Last updated