형 변환 (캐스팅, casting)
모든 변수와 리터럴에는 타입이 있습니다. 코드를 작성하다 보면 같은 타입뿐만 아니라 서로 다른 타입 간의 연산을 할 필요가 있습니다.
이럴 때는 연산을 수행하기 전에 타입을 일치 시켜야 하는데, 이러한 변수나 리터널의 하나의 타입을 다른 타입으로 바꾸는 것을 형변환 또는 타입 변환이라고 합니다.
즉 변수 혹은 리터널의 타입을 다른 타입으로 변환하는 것
예를 들어 int타입의 값과 float타입의 값을 연산해야 하는 경우, 먼저 두 값을 같은 타입으로 변환해야 합니다. 따라서 float타입으로 변환을 한 다음에 연산을 해야 합니다.
이때 int 타입이 아닌 float로 타입을 일치시킨 것 처럼 형 변환에는 규칙이 있습니다.
형 변환 (캐스팅, casting) 방법
(type) 피연산자
형변환의 방법은 아주 간단합니다.
형변환을 하고자 하는 변수나 리터널의 앞에 변환하고자 하는 타입을 괄호와 함께 붙여주기만 하면 됩니다.
double d = 85.4;
int score = (int)d; // double 타입의 변수 d를 int 타입으로 형변환
int score = (int)85.4; // 변수 d의 값을 읽어 와서 형변환한다.
int score = 85; // 형변환의 결과인 85를 변수 score에 저장한다.
int a = (char)65; // 결과 'A'
char b = (int)'A'; // 결과 65
float c = (int)1.6f; // 결과 1
int d = (float)10; // 결과 10.0f
위처럼 기본형에서 boolean을 제외한 나머지 타입들은 서로 형 변환이 가능합니다.
형 변환 (캐스팅, casting) 규칙
- 1) 두 피연산자 중 큰 타입으로 타입을 같게 일치해야 한다.
- int + long ▶ long
- float + int ▶ float
- float + double ▶ double
- 2) 피연산자의 타입 크기가 int보다 작으면 int로 변환시킨다.
- byte + short ▶ int
- char + short ▶ int
위의 규칙에 따라 int타입과 float타입의 연산에서는 두 타입 중 큰 값인 float로 타입을 같게 만든 것입니다.
만약에 byte의 크기가 작은 타입으로 형 변환을 시도할 경우에 Java Compiler는 오류를 발생시킵니다.
자동 형 변환
형 변환을 할 때 "(int) 변수명" 형식으로 형 변환할 캐스팅 연산자()를 지정해주는 방식이 "명시적 형변환"입니다.
그런데 경우에 따라 편의상의 이유로 형변환을 생략할 수 있습니다.
float f = 1234;
위와 같이 정수형 1234를 float형에 저장하는 경우에 서로 타입이 다르기 때문에 큰 타입인 float로 형변환을 해줘야 합니다. 그런데 형변환을 생략한 것을 볼 수 있는데, 값을 저장하는데 아무런 문제가 없습니다.
실직적으로는 float f = (float)1234; 가 실행되는 것 입니다.
이처럼 에러가 발생하지 않는 이유는 Java Complier가 자동적으로 형변환을 해주기 때문에 정상적으로 변수 f에 값이 저장되게 됩니다.
byte b = 1000; // error 발생
>> incompatible types possible lossy conversion from int to byte
그러나 위 처럼 변수가 저장할 수 있는 값보다 큰 범위의 값을 저장하려는 경우에는 에러가 발생하게 됩니다. 에러 메시지를 보면 위에서 말했듯이 "큰 타입에서 작은 타입으로의 형변환은 값 손실이 발생할 수 있다 " 라는 의미입니다.
int i = 3;
double d = 1.0 + i; // double d = 1.0 + (double)i;에서 형변환이 생략됨
위와 같은 계산식에서 주로 형변환이 생략되는데, 서로 다른 타입을 먼저 일치시킨 다음에 연산을 수행해야 하기 때문에 연산과정에서 형변환이 자동적으로 이루어지게 됩니다.
이처럼 연산과정에서 이루어지는 자동적 형 변환을 "산술 변환"이라고 합니다.
표현범위가 좁은 타입에서 넓은 타입으로 형 변환을 하는 경우에는 값 손실이 발생하지 않기 때문에 Java Compiler는 위와 같은 방향으로 자동적으로 형변환을 시도하게 됩니다.
즉 위 그림에서 왼쪽에서 오른쪽으로의 변환은 형 변환 연산자를 사용하지 않고 자동 형 변환이 되며, 반대의 경우 명시적 형 변환을 해줘야 합니다.
이를 정리하면 위와 같은 그림으로 표현할 수 있습니다.
그런데 조금 이상한 점이 있습니다. float형과 int형, long형과 double형은 byte의 크기가 같지만, 그림상으로는 실수형이 더 크다고 표현하고 있습니다.
이는 실수형은 정수형과 값을 표현하는 방식이 다르기 때문입니다.
같은 크기 일지라도 실수형이 정수형보다 훨씬 더 큰 표현 범위를 갖기 때문에 float와 double이 같은 크기인 int와 long보다 오른쪽에 존재하는 것 입니다.
char와 short 타입 크기
char와 short은 둘 다 2 byte의 크기로 크기가 같지만, char의 범위는 ('0~2^16-1' == 0 ~ 65,535)이고, short의 범위는 ('-2^15 ~ 2~15*-1' == -32768 ~ 32767)이므로 서로 범위가 달라서 어느 쪽으로 형 변환을 시도해도 값 손실이 발생하기 때문에 자동 형 변환이 수행될 수 없습니다.
char와 byte타입 크기
위 [타입의 값] 그림을 보면 char타입은 2Byte의 크기를 가지고, byte타입은 1Byte의 크기를 가지는 것을 볼 수 있습니다.
char 타입이 byte타입보다 더 크니깐 담을 수 있을 것 처럼 보이지만 실제로 byte 타입을 변환하여 char 타입에 저장할 수는 없습니다.
왜냐하면 char의 범위는 ('0~2^16-1' == 0 ~ 65,535)으로 음수를 표현할 수 없기 때문입니다.
public CharToCode {
public static void main(String[] args){
char c = 'A'; // 65
System.out.print(c);
}
위 코드처럼 char 타입은 유니코드와 아스키코드를 표현할 목적을 가지고 있기 때문에 음수는 필요가 없어서 음수를 표현할 조합까지 양수를 더 많이 조합하는 것에 사용합니다.
정수형 간의 형 변환
위 에서 말했듯이 큰 타입 ▶ 작은 타입으로 형변환을 할 경우에 오류가 발생한다고 했습니다.
이러한 오류를 "값 손실(loss of data)라고 부르며, 크기의 차이 만큼 값 손실이 발생합니다.
class Main {
public static void main(String[] args) {
int num = 300;
System.out.println( (byte)num ); // 결과 : 44
}
}
예를 들어 int 타입을 byte타입으로 변환을 시도하는 경우에 위와 같이 300 ➡ 44로 값손실이 발생했습니다.
반대로 작은타입(byte)에서 큰 타입(int)로 형 변환을 시도할 경우에는 빈 공간은 부호에 맞춰서 0 또는 1로 채워지게 됩니다.
원래의 값을 채우고 남은 빈 공간은 0으로 채우는 것이 일반적이지만, 변환하려는 값이 만약에 "음수"라면 형 변환을 한 이후에도 부호를 유지할 수 있도록 빈 공간을 1로 채우게 됩니다.
'Language > Java' 카테고리의 다른 글
[JAVA] Stream API에 대해 알아보기 _ Stream 생성 (2/5) (0) | 2024.05.16 |
---|---|
[JAVA] Stream API에 대해 알아보기 (1/5) (0) | 2024.05.15 |
[JAVA] 큰 수 다루기 (BigInteger, BigDecimal) (0) | 2024.05.13 |
[JAVA] char에서 String으로 변환하기 (value of() , charAt()) (0) | 2024.05.12 |
[JAVA] 입출력, BufferedReader, StringTokenizer, StringBuilder 알아보기 (0) | 2024.05.12 |