형변환은 리터럴(값)의 타입을 다른 타입으로 변환하는 것이다. 변수(저장공간)의 타입은 변환시키지 않는다.
컴퓨터는 같은 타입 간의 연산만 가능하다. 그래서 다른 타입 간의 연산을 수행해야 할 때는 '형변환'을 통해 타입을 일치시킨다. 형변환에는 수동 형변환(명시적 형변환)과 자동 형변환이 있다.
수동 형변환은 개발자가 직접 형변환 연산자(자료형)
을 이용해 형변환하는 방법이다. 자료형
에 변환하고자 하는 자료형을 넣으면 된다.
실수를 형변환하여 정수형 변수에 저장하는 예제이다.
public class Casting {
public static void main(String[] args) {
double d = 3.14;
int i = (int)d;
System.out.println(d); // d=3.14
System.out.println(i); // i=3
}
}
실수형 d
가 정수형으로 변환되어 소수점 부분은 버려진 값이 i
에 저장되었다. 형변환 연산이 수행된 값이 i
에 저장되는 과정은 다음과 같다.
1. int i = (int)d;
2. int i = (int)3.14;
3. int i = 3;
위 코드에서 알 수 있듯, 형변환 연산자는 피연산자의 값을 지정된 타입으로 변환하고 그 결과를 반환할 뿐이다. 형변환 연산자는 변수의 타입을 바꾸거나 변수에 저장된 리터럴의 타입을 바꾸어 다시 저장하는 것이 아니다. 그저 값을 반환한다. 그래서 피연산자인 d
의 값은 형변환 후에도 변화가 없다.
자동 형변환은 개발자가 형변환 연산자를 생략해도 컴파일러가 자동으로 추가해준다. 변수의 저장범위가 저장하려는 값보다 크면 자동 형변환이 가능하다. 그래서 자동 형변환은 다음 두 가지 규칙에 따라 발생한다.
float f = 1009; // 1009앞에 (float)가 생략됨
위 문장에서 우변은 int
타입이고 이 값을 저장하려는 변수의 타입은 float
이다. 정수형보다 실수형의 저장 범위가 더 넓다. 그래서 우변의 값이 float
로 자동 형변환 된다.
byte b = 130; // 에러, 우변이 byte타입의 범위(-128~127)을 벗어남
변수가 저장할 수 있는 값의 범위보다 더 큰 값을 저장하려는 경우, 형변환을 생략하면 에러가 발생한다.
double d = 5*3.5
우변은 정수와 실수의 곱이고, 그 결과를 double
타입의 변수 d
에 저장한다. 같은 타입간의 연산만 가능하므로 정수인 5
를 범위가 더 넓은 타입인 double
로 자동 형변환하여 연산한다. 만일 3.5
를 int
형으로 변환한다거나 우변의 결과인 17.5
를 int
형으로 변환한다면 소수점이 버려지는 '값손실'이 발생할 것이다.
int
보다 작은 타입이면 int
로 변환첫 번째 규칙은 피연산자의 값 손실을 최소화하기 위한 것이고, 두 번째 규칙은 int
보다 작은 타입(char, byte, short)
의 표현 범위가 좁아서 연산 중 오버플로우가 발생할 수 있기 때문에 존재한다.
아래와 같은 코드를 작성하면 에러가 발생한다.
byte a = 10; // int형으로 자동 형변환
byte b = 20; // int형으로 자동 형변환
byte c = a + b; // 에러
a
와b
모두 int
형보다 작은 byte
형이기 때문에 자동 형변환 두 번째 규칙에 따라 피연산자들을 int
형으로 바꾸어 연산했다. 그래서 a+b
의 결과도 int
형이다. 그러나 4byte인int
형의 값을 1byte인 byte
형의 c
에 저장하려 했기 때문에 에러가 발생하는 것이다.
크기가 작은 자료형의 변수를 큰 자료형의 변수에 저장할 때는 자동으로 형변환되지만, 반대의 경우에는 명시적으로 형변환 연산자를 사용해야한다. 따라서 위의 세번째 코드를 byte c =(byte)(a + b)
로 수정해야 에러가 발생하지 않는다.
다음과 같은 경우에는 에러가 발생하지는 않지만 의도한 값과 다른 값이 저장될 수 있다.
public class Casting {
public static void main(String[] args) {
int a = 1_000_000; // 1백만
int b = 2_000_000; // 2백만
long c = a*b;
System.out.println(c); //a*b = 2조, 그러나 출력은 -1454759936
}
}
a*b
의 결과를 담는 변수c
의 타입이 long
타입이기 때문에 를 저장하기에 충분하므로 2_000_000_000_000
이 출력될 것 같지만 오버플로우가 발생하여 다른 값이 출력된다. 그 이유는 a*b
의 결과가 이미 int
타입의 값이므로 long
타입으로 자동 형변환되어도 값은 변하지 않기 때문이다. 그저 오버플로우된 값이 출력된다.