타입 변환 (Type Conversion)
하나의 타입을 다른 타입으로 바꾸는 것을 타입 변환 혹은 형변환 이라고 한다.
예를 들어 int 타입을 double 타입으로 변환하거나 반대로 double 타입을 int 타입으로 변환하는 행위를 말한다.
프로그램에서 값의 대입이나 연산을 수행할 때는 동일 타입끼리만 가능하다.
그래서 같은 정수일지라도 타입이 다르면 연산을 수행하기 전에 같은 타입으로 만들어야 한다.
타입 변환에는 규칙이 있다.
메모리에 할당받은 바이트의 크기가 상대적으로 작은 타입에서 큰 타입으로의 타입 변환은 생략할 수 있다.
하지만 메모리에 할당받은 바이트의 크기가 큰 타입에서 작은 타입으로의 타입 변환은 데이터의 손실이 발생하게 된다. (표현 할 수 있는 범위가 다르기 때문이다.)
따라서 상대적으로 바이트의 크기가 작은 타입으로 타입 변환을 할 경우 자바 컴파일러는 오류를 발생시킨다.
편하게 이해 할 수 있는 방법이 있다. 큰 물컵과 작은 물컵에서 물을 옮기는 행위를 생각해보자.
int 타입과 long 타입으로 예를 들어보자면, 큰 물컵(long)의 물을 작은 물컵(int)으로 옮겨 담을 때, 작은 물컵이 담을 수 있는 물보다 많이 옮긴다면, 작은 물컵으로 옮겨 담을 때 물이 넘치게 된다. (데이터가 손실된다.)
하지만 작은 물컵에서 큰 물컵으로 물을 옮긴다면 양이 그대로 보존되어서 문제가 없다.
이렇게 형변환은 크기가 다른 두 물컵을 생각하면 이해 하기 쉽다.

short smallCup; // 작은 물컵
int bigCup; // 큰 물컵
bigCup = 100000000; // 1억
smallCup = (short)bigCup; // 큰 컵에서 작은 컵으로 물을 옮긴다.(형변환)
System.out.println(smallCup); // -7936 물이 넘쳐 이상한 값이 된다.
smallCup = 1000;
bigCup = (int)smallCup; // 작은 물컵의 물을 큰 물컵에 담는다.
System.out.println(bigCup); // 1000 물을 잘 옮겨 담았다.
자바의 형변환 방법에는 크게 두가지 종류가 있다.
개발자가 지정하지 않아도 자동적으로 이루어지는 자동(묵시적) 형변환과,
개발자가 명시해야만 이루어지는 강제(명시적) 형변환이 있다.
이 두 형변환 종류는 다른 자료형 간의 연산시 어떤 크기의 자료형에 맞추느냐에 따라 사용처가 달라지게 된다.
- 낮은 자료형으로 맞출 시 : 낮은 자료형으로 강제 형변환
- 높은 자료형으로 맞출 시 : 높은 자료형으로 자동 형변환
자동 형변환 (Promotion)
자동 타입 변환은 묵시적 / 암시적 형변환 이라고도 불리우며, 프로그램 실행 도중에 컴파일러가 자동적으로 타입 변환이 일어나는 것을 말한다.
단, 작은 크기를 가지는 타입이 큰 크기를 가지는 타입에 저장될 때만 자동 타입 변환이 발생한다.
자바에서는 데이터의 손실이 발생하지 않거나, 데이터의 손실이 최소화되는 방향으로 자동 타입 변환을 진행하기 때문이다.
즉, 기존의 값을 최대한 보존할 수 있는 큰 타입으로 자동 형변환이 가능하다고 이해하면 된다.

예를 들어 byte 타입은 1byte 크기를 가지고, int 타입은 4byte 크기를 가지므로 int 타입이 큰 크기 타입이고, byte 타입이 작은 크기 타입이다.
즉, 아래 코드와 같이 서로 다른 타입의 데이터라도 낮은 자료형과 높은 자료형을 연산할 경우 자동으로 형변환이 되어 계산된다.
byte a = 25;
// 1 byte로 저장된 값을 2 byte로 변환
short b = a;
// 2 byte로 저장된 값을 4 byte로 변환
int c = b;
// 4 byte로 저장된 값을 8 byte로 변환
long d = c;
반대로 크기가 큰 자료형의 값을 크기가 작은 자료형에 넣어주려고 하면 데이터의 손실이 발생한다(에러발생).
작은 타입과 큰 타입을 구분하는 기준은 각 데이터 타입의 메모리 크기(byte) 이다. 위의 사진과 같이 int타입은 메모리 크기가 4바이트고, short 타입은 2바이트이므로 int가 short보다 큰 타입이 되게 된다. 반면 long은 8바이트이므로 int가 long보다는 작은 타입이 된다.
Tip*
long 과 float 타입 크기
그렇다면 이렇게 질문하는 개발자가 있을 것이다.
long 타입은 8바이트이고 float은 4바이트이기 때문에 따라서 float가 long 보다는 작은 타입이지만, 그림상 long 보다 float이 더 크다고 되어있다.
왜냐하면 일반적으로 메모리 설계상 정수 타입보다 실수 타입이 더 크게 되어 있기 때문이다.
타입 크기를 확인해보자.

복잡한 식이 작성 되어 있지만, 프로그래밍을 통하여 비교 할 수 있다.
double a = 9223372036854775808.0; // 비교를 위해 실수로 표현
double b = 3.4 * (Math.pow(10, 38)); // 3.4 x 10의38승
System.out.println(a - b < 0); // True -> a-b가 음수가 나왔으니 b가 더 크다.
4byte float이 더 큰 수를 표현할 수 있는 이유는 부동소수점 방식으로 표현하기 때문이다. (추후에 포스팅 하겠다.)
char 와 byte 타입 크기
타입 크기 비교에 또다른 예외가 존재한다.
char 타입(문자 자료형 이지만 사실 아스키 코드 숫자를 저장하기때문에 정수형 타입이다.)과 byte 타입은 둘 다 정수형 타입이고, char타입은 2바이트의 크기, byte타입은 1바이트의 크기를 가진다.
char 타입이 byte 타입보다 더 크니까 담을수 있어보이지만, 실제로 byte 타입을 변환하여 char타입에 저장할 수는 없다.
왜냐하면 char타입은 음수를 표현할 수 없기 때문이다.
연산식 자동 형변환
데이터 연산은 기본적으로 같은 타입의 피연산자 간에만 수행되기 때문에, 서로 다른 타입의 피연산자가 있을 경우 두 피연산자 중 크기가 큰 타입으로 자동 형변환된 후 연산을 수행한다.
예를 들어 int 타입 피연산자와 double 타입 피연산자를 덧셈 연산하면, int타입이 먼저 double 타입으로 변환되고 연산이 수행되게 된다. 그리고 결과는 double 타입이 된다.
자바는 피연산자를 4바이트 단위로 저장하기 때문에, 정수 연산일 경우 int 타입을 기본으로 하는 특징이 있다.
그래서 2바이트 char 타입이라도 둘이 연산을 하게되면 자동으로 int 타입으로 변환되어 계산 된다.
char ch1 = 'A';
char ch2 = 'B';
int result1 = ch1 + ch2; // 'A'의 유니코드 값과 'B'의 유니코드 값을 더한 값 저장
// char result2 = ch1 + ch2; - 에러 발생 !!!
byte byteValue1 = 10;
byte byteValue2 = 20;
// byte byteValue3 = byteValue1 + byteValue2; - 에러
int intValue1 = byteValue1 + byteValue2;
강제 형변환 (Casting)
강제 형변환은 명시적 형변환이라고 불리우며, 사용자가 타입 캐스트 연산자 를 사용하여 값의 타입을 강제적으로 변환을 수행하는 것을 말한다.
작은 크기의 타입은 큰 크기의 타입으로 자동 형변환이 되지만, 반대로 큰 크기의 타입은 작은 크기의 타입으로 자동 타입 변환을 할 수 없다.
예를 들어, 4byte인 int타입을 1byte인 byte 타입에 담을 수 없다. 마치 큰 컵의 물을 작은 컵 안에 모두 넣을 수 없는 것과 동일한 이치이다. 큰 그릇에 있는 물의 양에 따라 물이 넘칠수도 있고 보존될 수도 있다. 마찬가지로 큰 데이터 타입에서 작은 데이터 타입으로 옮길 때 데이터의 손실이 발생할 수도 아닐수도 있다. 하지만 필요에 의해서 int타입을 byte 타입으로 처리해주어야 할 필요성이 생긴다면 강제 형변환을 고려해야 한다.
int num = 60000;
byte value = (byte)num; // 데이터 손실 발생
System.out.println(value); // 96
int num2 = 10;
byte value2 = (byte)num2; // 데이터 손실 발생하지 않음
System.out.println(value2); // 10
주의할 점은, 강제적인 동작이므로 만일 데이터의 손실이 일어난다면 정확한 연산을 수행할 수 없기 때문에 예상하지 못한 결과를 얻을 수 있다는 것이다.
byte b = 100;
byte b = (byte)100; // byte의 범위는 -128 ~ 127이기 때문에 데이터 손실 없이 대입이 가능하고 컴파일러가 자동 형변환을 해준다.
---------------------------------------
int i = 100;
byte b = i; // 에러, 변수이기 떄문에 상수일 때랑 다르다. 컴파일러는 변수 안에 값을 알지 못하기 때문에
byte b = (byte)i;
---------------------------------------
byte b = 1000; // 에러, byte 타입의 범위를 넘었기 때문에 자동 형변환 x
byte b = (byte)1000; // b에 -24가 대입된다.
실수 타입을 정수 타입으로 강제 타입 변환하면 소수점 이하의 데이터들은 버려진다.
double dbNum = 12.3456;
int num = (int)dbNum; // 12
캐스트 연산자를 사용한 형변환은 일시적이기 때문에 피연산자의 자체 자료형에는 영향을 주지 않는다.
그리고 기본 자료형 중 boolean을 제외하면 모든 자료형은 형변환이 가능하다.
'Language > Java' 카테고리의 다른 글
| [JAVA] 변수의 기본형 & 참조형 타입 (0) | 2023.09.06 |
|---|