형변환이란 무엇인가
자바는 정적 타입 언어입니다. 모든 값에는 타입이 있고, 컴파일러는 호환되지 않는 타입을 조용히 섞어 쓰도록 허용하지 않습니다. 형변환은 값을 한 타입에서 다른 타입으로 바꾸는 방법입니다. 자바가 대신 해 줄 때도 있고, 명시적으로 요청해야 할 때도 있습니다.
방향은 두 가지입니다. 작은 타입에서 큰 타입으로 옮기는 것(int를 double로)은 안전하며 자동으로 일어납니다. 큰 타입에서 작은 타입으로 옮기는 것(double을 int로)은 데이터를 잃을 수 있으므로, 자바는 캐스팅으로 명시하도록 강제합니다.
확대 변환: 자동 변환
확대 변환은 값을 더 여유 있는 타입으로 옮깁니다. 아무것도 잃을 수 없으므로 자바가 캐스팅 없이 대신 처리합니다:
int 7은 double에 들어가 7.0이 됩니다. 숫자의 확대 순서는 byte -> short -> int -> long -> float -> double이며, 이 사슬을 따라 왼쪽에서 오른쪽으로 대입할 때는 절대 캐스팅이 필요하지 않습니다. 산술 연산에서 타입을 섞어도 대개 "그냥 동작하는" 이유가 바로 이것입니다. 자바가 더 작은 피연산자를 먼저 확대하기 때문입니다.
축소 변환: 명시적 캐스팅
반대 방향, 즉 축소 변환은 데이터를 잃을 수 있으므로, 값 앞에 대상 타입을 괄호로 붙이지 않으면 컴파일러가 거부합니다:
(int) pi가 캐스팅입니다. 무엇을 하는지 주목하세요. 0 방향으로 잘라내어 소수 부분을 그냥 버립니다. 3.9는 3이 되고, -3.9는 -3이 됩니다. 반올림하지 않습니다. 반올림하고 싶다면 그것은 다른 도구입니다:
캐스팅이 잘라낸다는 것을 잊는 것은 초보자가 가장 흔히 겪는 버그 중 하나입니다. "반올림한" 숫자가 항상 1만큼 작다면, 대개 이것이 원인입니다.
정수 나눗셈의 함정
이 함정에는 누구나 한 번은 빠집니다. 두 피연산자가 모두 int일 때 / 연산자는 정수 나눗셈을 수행하고 나머지를 버립니다. 그것도 double에 대입되기 전에 그렇게 됩니다:
a / b는 7 / 2이며, 전부 int 연산으로 계산되어 3이 됩니다. 그 후에 double로 확대해도 3이 3.0이 될 뿐, .5는 이미 사라졌습니다. 먼저 한쪽 피연산자를 double로 캐스팅하면((double) a / b) 식 전체가 부동소수점 연산이 되어 원하던 3.5를 얻습니다. 한쪽만 캐스팅하면 되며, 자바가 나머지 한쪽을 맞춰서 확대합니다.
축소가 오버플로될 때
캐스팅은 값이 들어맞는지 확인하지 않습니다. 들어맞지 않으면 비트가 그냥 잘려나가고 놀라운 결과를 얻게 됩니다. 오류도 경고도 없습니다:
300은 byte에 들어가지 않으므로 상위 비트가 버려져 44가 됩니다. 이것은 "조용한" 데이터 손실입니다. 프로그램은 잘 실행되면서 틀린 답을 내놓습니다. 값이 더 작은 타입에 확실히 들어맞을 때만 축소하세요.
숫자와 문자열 간의 변환
String은 숫자가 아니라 객체이므로 (int)로 캐스팅할 수 없습니다 - 그것은 컴파일 오류입니다. 변환에는 대신 전용 메서드를 사용합니다:
텍스트에서 숫자로 갈 때는 Integer.parseInt / Double.parseDouble을 사용합니다. 문자열이 유효한 숫자가 아니면(예: "hello") 이 메서드들은 NumberFormatException을 던집니다. 숫자에서 텍스트로 갈 때는 String.valueOf(n)을 사용하거나 그냥 ""와 연결하면 됩니다. n + ""가 int를 String으로 변환하기 때문입니다. "5" + 1(문자열 "51"이 됨)과 5 + 1(6이 됨)의 차이에 주의하세요. String이 관여하는 순간 +는 연결을 의미합니다.
객체 캐스팅
캐스팅은 기본형뿐 아니라 객체 참조에도 적용됩니다. 상속 사슬을 따라 참조를 관련된 타입으로 캐스팅할 수 있습니다. 부모 타입으로 확대하는 것은 자동이지만, 다시 아래로 축소하는 것은 명시적 캐스팅이 필요하며 객체가 실제로 그 타입일 때만 안전합니다:
Object o = "hello";
String s = (String) o; // OK: o는 실제로 String을 참조함
Integer bad = (Integer) o; // 컴파일은 되지만 런타임에 ClassCastException을 던짐
두 번째 캐스팅은 컴파일은 되지만 객체가 Integer가 아니라 String이므로 런타임에 ClassCastException으로 터집니다. 상속과 다형성에 이르면 이것을 제대로 만나게 됩니다. 지금은 (Type) 문법이 객체에 적용된 동일한 개념이라는 것만 알아 두면 됩니다.
자주 묻는 질문
자바에서 형변환이란 무엇인가요?
형변환은 값을 한 데이터 타입에서 다른 타입으로 변환하는 것입니다. 자바는 작은 "확대" 변환(예: int에서 double로)은 자동으로 처리하지만, 데이터를 잃을 수 있는 "축소" 변환(예: double에서 int로)은 명시적 캐스팅이 필요합니다: int n = (int) 3.9;.
자바에서 double을 int로 변환하려면 어떻게 하나요?
대상 타입을 값 앞에 괄호로 적습니다: int n = (int) 3.9;. 이는 0 방향으로 잘라내므로(소수 부분을 버림) 4가 아니라 3을 얻습니다. 대신 반올림하려면 Math.round(3.9)를 사용하면 4를 반환합니다.
자바에서 String을 int로 변환하려면 어떻게 하나요?
String은 숫자가 아니므로 (int)로 캐스팅할 수 없습니다. int를 얻으려면 Integer.parseInt("42")를, double을 얻으려면 Double.parseDouble("3.14")를 사용하세요. 텍스트가 유효한 숫자가 아니면 이 메서드들은 NumberFormatException을 던집니다.