형변환이란 무엇인가
자바는 정적 타입 언어입니다. 모든 값에는 타입이 있고, 컴파일러는 호환되지 않는 타입을 조용히 섞어 쓰도록 허용하지 않습니다. 형변환은 값을 한 타입에서 다른 타입으로 바꾸는 방법입니다. 자바가 대신 해 줄 때도 있고, 명시적으로 요청해야 할 때도 있습니다.
방향은 두 가지입니다. 작은 타입에서 큰 타입으로 옮기는 것(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) 문법이 객체에 적용된 동일한 개념이라는 것만 알아 두면 됩니다.
다음: if-else
캐스팅은 값을 다시 빚어내게 해 줍니다. 다음 단계는 그 값들을 바탕으로 프로그램이 경로를 선택하게 만드는 것입니다. if-else 문은 조건이 참일 때 한 블록을, 거짓일 때 다른 블록을 실행합니다 - 코드가 내리는 모든 결정의 토대입니다. 그것이 다음 페이지입니다.
자주 묻는 질문
자바에서 형변환이란 무엇인가요?
형변환은 값을 한 데이터 타입에서 다른 타입으로 변환하는 것입니다. 자바는 작은 "확대" 변환(예: 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을 던집니다.