[Java] 자료형의 형변환
자료형의 형변환에 대해 알려면 우선 자료형 과 변수와 상수 에 대해서 알고 있어야 하므로, 이에 대해 모른다면 해당 문서를 먼저 읽고 오는 것을 추천한다.
변수에 리터럴 상수 대입 시 형변환
리터럴 상수도 마찬가지로 자료형을 가지고 있다. 정수형 리터럴 상수는 int 형으로, 실수형 리터럴 상수는 double형으로 정해져 있다.
byte num = 1;
따라서 위 코드의 경우, byte 자료형인 num 변수에 int 자료형인 리터럴 상수 1을 대입한 것이다. 이 과정에서 int형이 byte 자료형으로 변한 뒤 저장된 것이다. 이 과정에서 사용자는 아무런 행동도 취하지 않았고, 이러한 자료형의 형변환은 컴파일러가 자동으로 한 것이다. 이렇게 컴파일러에 의해 자동으로 형변환이 일어나는 것을 “자동 형변환” 또는 “묵시적(암시적) 형변환”이라고 표현한다.
byte 자료형은 127까지를 표현할 수 있고, int형인 리터럴 상수 1은 그 메모리 용량이 4바이트라 할지라도 앞의 3바이트를 잘라도 데이터가 오염(훼손)되지 않기에 범위가 더 작은 byte 형으로의 형변환이 가능한 것이다.
일반적으로 생각했을 때, 크기가 작은 자료형에서 크기가 큰 자료형으로의 형변환을 해도 데이터가 오염되지 않는다. 예를 들어 byte 형 변수에 1이 할당된 경우, 메모리 상에서는 00000001로 저장된다. 이 변수를 int형으로 바꾸면 00000000 00000000 00000000 00000001이 된다. 즉, 앞에 모두 0인 1바이트가 더 추가될 뿐, 기존의 바이트를 삭제, 수정하지 않기에 데이터는 그대로 보존된다. 그러므로 자동 형변환은 보통 자료형 범위가 작은 자료형에서 그 범위가 큰 자료형으로 형변환이 일어난다.
- byte → short → int → long → float → double
- char → int
또한, long → float 에서 알 수 있듯, 정수형에서 실수형으로 자동 형변환이 될 수 있다.
위에서 언급한 방향의 역방향은 기존의 데이터를 구성하는 바이트 일부를 삭제하는 과정이 포함되어 있기에 데이터가 오염될 수 있어 자동 형변환이 되지 않고 에러가 발생할 수 있다. 다만, 앞서 byte num = 1;
에서 알 수 있듯, 특정 자료형의 변수에 데이터가 할당될 때 그 데이터의 범위가 해당 자료형의 범위 안이라면 데이터의 바이트 일부가 절삭되더라도 그 데이터가 보존되므로 (00000000 00000000 00000000 00000001 → 00000001 이 과정에서 앞의 3바이트가 절삭되었어도 맨 뒤의 바이트에 저장된 이진 정보는 변하지 않았다) 자동 형변환이 가능한 것이다. 만약 byte num = 128;
인 경우, 해당 데이터는 byte 자료형의 범위 밖이므로 에러가 발생한다.
다음의 코드를 보자.
public class App {
public static void main(String[] args)
{
byte a = (byte)127;
byte b = (byte)128;
System.out.println(a);
System.out.println(b);
}
}
127
-128
위 예제에서, 리터럴 상수 앞에 (byte)라 작성하였는데, 이는 해당 데이터의 자료형을 byte 형으로 변환하여 해당 변수에 할당하라는 뜻이다. 이것은 사용자가 명시적으로 형변환을 지시했으므로 이 경우 “강제 형변환”, “명시적 형변환”이라 부른다.
위 예제의 코드와 그 실행결과를 보면, 변수 b의 경우, byte 자료형이 표현할 수 있는 데이터의 범위를 넘는 128이라는 수가 b에 할당되었음에도 에러 없이 실행되었다. 이는 앞에 (byte)를 기입함으로써 명시적 형변환을 일으켰기 때문이다. 그런데 결과값이 음수가 나오는 이상한 현상이 발생했다. 앞서 말했듯, 일반적으로 크기가 큰 자료형에서 크기가 더 작은 자료형으로 형변환 시 데이터가 오염될 수 있음을 언급했다. 이러한 이유로 엉뚱한 값이 출력되는 것이다. 따라서 명시적 형변환에 의한 데이터 오염 문제는 에러가 발생하지 않으니 사용자가 각별히 주의해야 한다.
리터럴 상수의 형변환에 대해 주의해야할 점이 하나 더 있다.
public class App {
public static void main(String[] args)
{
int a = 2147483648;
System.out.println(a);
}
}
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The literal 2147483648 of type int is out of range
at App.main(App.java:4)
int 자료형의 양의 범위는 2147483647까지인데, 위 예제에서는 1이 더 큰 수를 입력했기에 에러가 발생하였다. 그러면 변수 a의 자료형을 long으로 변환시키면 에러가 발생하지 않을까?
public class App {
public static void main(String[] args)
{
long a = 2147483648;
System.out.println(a);
}
}
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The literal 2147483648 of type int is out of range
at App.main(App.java:4)
여전히 에러가 발생한다. 왜일까? 앞서 언급했듯, 리터럴 상수에 속하는 정수는 기본적으로 int 자료형이다. 따라서 위 예제의 경우, 해당 리터럴 상수는 그 숫자가 long 자료형의 범위 안에 속하기는 하나, 데이터 자체가 int 형이므로 이를 long 형으로 형변환을 해야 컴파일러가 제대로 인식할 수 있다.
public class App {
public static void main(String[] args)
{
long a = 2147483648l;
System.out.println(a);
}
}
2147483648
위 예제에서처럼, 리터럴 상수 뒤에 영문자 l (또는 대문자 L이라고 해도 된다)을 붙이면 해당 리터럴 상수를 long형으로 변환한 후에 변수 a에 할당되므로, 문제없이 실행된다.
이는 float형 변수에 실수형 리터럴 상수를 할당할 때에도 마찬가지이다.
public class App {
public static void main(String[] args)
{
float pi = 3.14;
System.out.println(pi);
}
}
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Type mismatch: cannot convert from double to float
at App.main(App.java:4)
앞서 언급했듯, 실수형 리터럴 상수는 기본적으로 double형으로 지정되어 있으므로, double형 데이터를 float형 변수에 대입하려고 하니 자료형이 맞질 않아 에러가 발생한 것이다. 이 경우, 할당하려는 데이터의 자료형을 float형으로 형변환시켜줘야 한다. 이 경우, 데이터 뒤에 영문자 f 또는 F를 기입하면 해당 데이터가 float 형으로 형변환된다.
public class App {
public static void main(String[] args)
{
float pi = 3.14f;
System.out.println(pi);
}
}
3.14
정리하자면, 변수에 값을 할당할 때에는 변수의 자료형과 데이터의 자료형이 일치해야 한다. 다만, 그 크기가 작은 자료형은 크기가 더 큰 자료형으로 자동 형변환이 가능하므로, 변수의 자료형보다 그 변수에 할당될 데이터의 자료형의 크기가 더 작아도 자동 형변환이 되어 문제없이 실행된다. 반면, byte num = 1; 과 같이 데이터의 자료형이 변수의 자료형보다 더 크더라도, 형변환 시 데이터가 오염되지 않는다면, 즉, 데이터의 범위가 변수의 자료형 범위 안에 든다면 문제 없이 자동 형변환이 된다.
어쨌거나 형변환이 되는 것과 안되는 것을 구분하는 기준은 형변환 했을 때 데이터가 훼손되느냐 아니냐에 의존하는 것임을 알 수 있다.
참고로, int → double 형으로의 자동 형변환이 가능하므로 다음의 코드는 에러 없이 잘 작동된다.
public class App {
public static void main(String[] args)
{
double thisYear = 2024;
System.out.println(thisYear);
}
}
2024.0
위 예제의 경우, int 형 리터럴 상수 2024가 double형 리터럴 상수 2024.0으로 자동 형변환된 뒤 thisYear 변수에 할당되었음을 알 수 있다.
연산 시의 형변환
연산 시에도 형변환이 일어난다. 사칙연산 등 두 개의 피연산자를 필요로하는 연산자들의 경우, CPU 내 연산장치는 기본적으로 두 피연산자의 자료형이 서로 동일해야 계산을 할 수 있으며, int 이상의 자료형부터 연산이 가능하다. 즉, int, long, float, double 자료형들만 연산 가능하며, int형 피연산자들끼리, float형 피연산자들끼리 사이에서만 연산이 가능하다.
두 피연산자의 자료형이 다를 경우, 둘 중 자료형의 크기가 더 큰 쪽으로 자동 형변환이 일어난 후에 연산된다. 그리고 byte, short는 int로 형변환된 뒤에 연산된다. 이 경우 연산 결과도 int 형일 수밖에 없다.
public class App {
public static void main(String[] args)
{
byte a = 5;
byte b = 10;
byte result = a + b;
System.out.println(result);
}
}
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Type mismatch: cannot convert from int to byte
at App.main(App.java:6)
위 예제의 경우, 에러를 피할려면 둘 중 하나를 해야 한다.
- result 변수에 할당될 데이터값 앞에 (byte)를 작성하여 명시적 형변환을 하거나,
- 아니면 result 변수를 byte 자료형이 아닌 int 자료형으로 수정하거나이다.
그러면 에러를 피할 수 있다. 다만, 첫 번째 방법의 경우, 데이터가 훼손되어 원하는 결과가 나오지 않을 수도 있음에 주의해야 한다.
public class App {
public static void main(String[] args)
{
int a = 1;
double b = 2.0;
int result = a + b;
System.out.println(result);
}
}
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Type mismatch: cannot convert from double to int
at App.main(App.java:6)
위 코드는 에러를 발생시킨다. int 형 숫자 1과 double형 숫자 2.0을 더할 때, int형 숫자가 double형 숫자로 1.0으로 바뀐 뒤에 연산이 실행된다. 그러므로 연산 결과도 당연히 double이다. 그런데 이를 자료형의 크기가 더 작은 int 형 변수 result에 할당하려고 하니 에러가 발생하는 것이다. 이 경우, result 변수의 자료형을 double로 바꾸거나, (int)(a+b)와 같이 작성해야 한다.
References
[1] 이재환, “이재환의 자바 프로그래밍 입문”, (골든래빗, 2021)
This content is licensed under
CC BY-NC 4.0
댓글남기기