설명
하나의 데이터 타입을 다른 타입으로 바꾸는 것을 타입 변환 혹은 형변환이라고 합니다.
기본적으로 자바에서는 대입 연산자 = 에서 변수와 값 서로 양쪽의 타입이 일치하지 않으면 할당이 불가능합니다.
프로그램에서 값의 대입이나 연산을 수행할 때는 같은 타입끼리만 가능하기 때문입니다.
long d = 10.233; // ERROR
그래서 다음과 같이 캐스팅 연산자를 사용하여 강제적으로 타입을 지정하여 변수에 대입하도록 해야 합니다.
long d = (long)10.233;
상속 관계의 클래스는 크게 부모클래스와 자식클래스로 구분할 수 있습니다.
기본형 타입을 서로 형변환 할 수 있듯이, 자바의 상속 관계에 있는 부모와 자식클래스 간에는 서로 간의 형변환이 가능합니다.
클래스는 Reference 타입으로 분류되니 이를 참조형 캐스팅(업캐스팅 / 다운캐스팅) 이라고 부릅니다.

한 가지 주의해야 할 점은, 같은 부모 클래스를 상속받고 있더라도 형제 클래스끼리는 아예 타입이 다르기 때문에 참조 형변환이 불가능합니다.
업캐스팅
업캐스팅은 자식 클래스 객체를 부모 클래스 타입으로 변환하는 것을 말합니다.
class Parent {
String name;
int age;
}
class Child extends Parent {
/*
String name;
int age;
*/
int number;
}
Parent p = new Parent();
Child c = new Child();
Parent p2 = (Parent)c; // 명시적 업캐스팅 - 자식에서 부모로
Parent p2 = c; // 암시적 업캐스팅 - 위에 업캐스팅과 같음
업캐스팅은 캐스팅 연산자 괄호를 생략할 수 있으며, 부모 클래스로 캐스팅된다는 것은 멤버의 개수 감소를 의미합니다.
이는 곧 자식 클래스에서만 있는 속성과 메서드는 실행하지 못한다는 것을 의미합니다.
Animal animal = (Animal) dog; // 명시적 캐스팅
Animal animal = dog; // 암시적 캐스팅 (생략 가능)
// 그리고 Dog 에서만 정의한 Jump() 메서드도 실행이 불가능
마지막으로 업캐스팅의 가장 큰 특이점은 업캐스팅을 하고 메소드를 실행할 때, 만일 자식 클래스에서 오버라이딩한 메서드가 있을 경우, 부모 클래스의 메서드가 아닌 오버라이딩 된 메서드가 실행됩니다.
오버라이딩 된 메서드 실행
class Animal {
void sound() {
System.out.println("Some sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Bark");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog(); // 업캐스팅
animal.sound(); // 출력: Bark
}
}
Dog 객체를 Animal 타입으로 업캐스팅 했지만 animal.sound() 호출 시, 실제 객체는 Dog 이므로 Dog의 sound()가 실행됩니다. ( 다형성 )
업캐스팅을 하는 이유
이렇게 업캐스팅을 하는 방법과 특징, 주의점은 알았지만 정작 왜 하는지는 모호할 수 있습니다.
업캐스팅을 사용하는 이유는 공통적으로 할 수 있는 부분을 만들어 간단하게 다루기 위해서 입니다.
// 부모 클래스
class Animal {
void sound() {
System.out.println("Some generic animal sound");
}
}
// 자식 클래스 1
class Dog extends Animal {
@Override
void sound() {
System.out.println("Bark");
}
}
// 자식 클래스 2
class Cat extends Animal {
@Override
void sound() {
System.out.println("Meow");
}
}
// 자식 클래스 3
class Cow extends Animal {
@Override
void sound() {
System.out.println("Moo");
}
}
public class Main {
public static void main(String[] args) {
// 다양한 Animal 객체 생성
Animal dog = new Dog(); // 업캐스팅
Animal cat = new Cat(); // 업캐스팅
Animal cow = new Cow(); // 업캐스팅
// 배열로 공통적으로 관리
Animal[] animals = {dog, cat, cow};
// 공통된 메서드 호출
for (Animal animal : animals) {
animal.sound(); // 각 동물의 소리를 출력
}
}
}
Dog, Car, Cow 객체를 각각 Animal 타입으로 업캐스팅하여 배열에 저장합니다.
배열의 타입은 Animal 이므로 모든 자식 클래스를 공통적으로 처리할 수 있으며, 부모 클래스 Animal의 메서드 sound()를 호출하면, 각 자식 클래스에서 오버라이드된 버전이 실행됩니다.
이렇게 하나의 타입으로 묶어 배열을 구성할 수 있어 코드량도 훨씬 줄어들고 가독성도 좋아지며 유지보수성도 좋아짐을 볼 수 있습니다.
여기서 또 궁금해지는 게 있습니다. 위에서 언급했던 것처럼 자식 클래스에만 있는 고유한 메서드를 실행하려면 어떻게 해야 할까요? 오버라이딩 한 메서드가 아닌 이상 업캐스팅한 부모 클래스 타입에서 자식 클래스의 고유 메서드를 실행할 수 없습니다. 따라서 업캐스팅한 객체를 다시 자식 클래스 타입으로 되돌리는 다운 캐스팅이 등장하게 되었습니다.
다운캐스팅
다운캐스팅은 거꾸로 부모 클래스가 자식 클래스 타입으로 캐스팅되는 것을 말합니다.
다운캐스팅은 캐스팅 연산자 괄호를 생략할 수 없으며, 다운캐스팅의 목적은 업캐스팅한 객체를 다시 자식 클래스 타입의 객체로 되돌리는데 목적을 두고 있습니다.
즉, 다운캐스팅의 진정한 의미는 부모 클래스로 업 캐스팅된 자식 클래스를 복후하여, 본인의 필드와 기능을 회복하기 위해 있는 것입니다.
class Animal {
void sound() {
System.out.println("Some generic animal sound");
}
}
class Dog extends Animal {
void sound() {
System.out.println("Bark");
}
void wagTail() {
System.out.println("Dog is wagging its tail");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog(); // 업캐스팅
Dog dog = (Dog) animal; // 다운캐스팅 ( 연산자 괄호를 절대 생략 X )
dog.sound(); // 출력: Bark (Dog의 메서드 호출)
dog.wagTail(); // 출력: Dog is wagging its tail (Dog 고유 메서드 호출)
}
}
문득 궁금해지는 게 왜 다운캐스팅은 캐스팅 연산자를 생략할 수 없을까?인데 나름의 이유가 있습니다.
다운캐스팅은 곧 사용할 수 있는 객체 멤버 증가를 의미하는데, 멤버의 증가는 불안전합니다. 왜냐하면 실제 참조변수가 가리키는 객체가 무엇인지 모르기 때문에 어떠한 멤버가 추가되는지 알 수 없기 때문입니다. 그래서 반드시 형변환 괄호를 기재함으로써 증가된 클래스의 멤버가 무엇인지 알게 하도록 개발자한테 알려줘야 하기 때문입니다.
다운캐스팅의 주의점
캐스팅의 목적은 업캐스팅한 객체를 되돌리는 데 있다고 했습니다.
이 의미는 업캐스팅하지 않은 생 부모 객체일 경우, 이를 그대로 다운캐스팅할 경우 오류( ClassCastException )가 발생합니다.
Unit unit = new Unit();
Zealot unit_down2 = (Zealot) unit; //! RUNTIME ERROR - Unit cannot be cast to Zealot
unit_down2.attack(); //! RUNTIME ERROR
unit_down2.teleportation(); //! RUNTIME ERROR
이런 다운캐스팅의 주의점은 컴파일 에러가 발생하지 않고 런타임 에러가 발생하는 큰 위험이 있습니다.
'Java' 카테고리의 다른 글
| String (0) | 2024.12.17 |
|---|---|
| Java - Abstract (1) | 2024.12.11 |
| 자바 공부할 때 도움이 되는 용어 설명 (0) | 2024.12.10 |
| 인터페이스 (0) | 2024.12.10 |
| List, ArrayList, LinkedList (2) | 2024.12.10 |