어떤 객체(인스턴스)를 다른 타입으로 변환
참조형 데이터타입간의 형변환
반드시 상속 관계가 전제되어야 함
(상속 관계가 아닌 타입끼리의 형변환X)
슈퍼클래스 타입 <-> 서브클래스 타입
묵시형 형변환(업캐스팅, Up Casting)
명시적 형변환(다운캐스팅, Down Casting)
슈퍼클래스 타입 레퍼런스로 서브클래스 인스턴스를 참조
= 서브클래스의 인스턴스를 슈퍼클래스 타입으로 변환
컴파일러에 의해 자동 형변환 일어남(=묵시적 형변환)
참조 가능 영역이 축소
(슈퍼클래스로부터 상속된 멤버만 접근 가능해짐)
서브클래스 인스턴스 멤버 중 공통 항목을 제외한 나머지에 대한 포기 선언을 하는 것
-> 대신 하나의 슈퍼클래스 타입으로 여러 서브클래스의 인스턴스 참조 가능
public static void main(String[] args) {
Parent p = new Parent(); // 슈퍼클래스 인스턴스 생성
p.parentPrn();
// parent 타입으로 접근 가능한 메서드는
// 슈퍼클래스의 메서드 하나!
Child c = new Child(); // 서브클래스 인스턴스 생성
c.parentPrn();
c.childPrn();
// child 타입으로 접근 가능한 메서드는
// 슈퍼클래스의 메서드와 서브클래스의 메서드 두 개!
p = c; // 업캐스팅 방법 1
Parent p = new Child(); // 업캐스팅 방법 2
// 이렇게 하려면 위에서 parent는 인스턴스 생성하면 안됨!
p.parentPrn();
// 업캐스팅 후에는 상속된 메서드만 접근 가능
// 서브클래스의 메서드는 안 보임
}
class Parent {
public void parentPrn() {
System.out.println("슈퍼클래스의 parentPrn()");
}
}
class Child extends Parent {
public void childPrn() {
System.out.println("서브클래스의 childPrn()");
}
}
업캐스팅 방법 1과 방법 2는 똑같을까?
X
p == c
로 인스턴스를 비교하면 방법 1은true
가 출력되고 방법 2는false
가 출력된다// 방법 1 p = c; // c의 주소값을 p에 넣는 거라서 인스턴스가 같음 // 방법 2 parent p = new Child(); // 새로운 인스턴스를 생성하는 것! // new가 붙었으니까! // 그래서 처음에 생성된 참조변수 c랑은 다른 인스턴스!
서브클래스의 레퍼런스가 슈퍼클래스의 인스턴스를 참조
= 슈퍼클래스의 인스턴스를 서브클래스 타입으로 형변환
참조 가능한 영역이 확대
자동 형변환이 일어나지X (강제 형변환 필요)
강제 형변환을 통해 구문 오류가 해결되더라도 실제 실행 시점에서 오류(실행 오류) 발생하게 됨
-> 존재하지 않은 영역의 참조 위험성 때문
=> 자바에서는 다운캐스팅 지원X
Parent p = new Parent();
p.parentPrn();
Child c; // 서브클래스 Child 타입 변수 선언
c = p; // 다운캐스팅, !!!컴파일 에러!!!
// 자동형변환 안되기 때문에 오류 발생함
c = (Child)p; // 강제형변환으로 가능
c.parentPrn(); // 원래 가지고 있던 메서드
c.childPrn(); // parent는 없던 메서드
// 존재하지 않는 영역에 대한 참조 위험성 때문에
// 자바에서 다운캐스팅 존재하지 않음!!!
다운 캐스팅이 가능한 경우
Parent p = new Parent();
p = new Child(); // 업캐스팅
p.parentPrn(); // 접근 가능한 메서드는 하나!
Child c = (Child)p; // 다운캐스팅
// 자동 형변환 안되기 때문에 강제형변환 수행
c.parentPrn();
// parent가 원래 가지고 있었던 메서드
// 다운캐스팅할 때 같이 가져옴
c.childPrn();
// Child가 원래 가지고 있었던 메서드라서 존재하는 영역임!
좌변의 객체가 우변 클래스 타입인지 판별
참조변수가 부모객체를 참조하는지 자식객체를 참조하는지 판별 가능
판별 결과가 true
면 형변환 가능
판별 결과가 false
면 형변환 불가능
우변의 클래스 타입과 같거나 우변 클래스 타입의 자식 타입 모두 가능
A is a B = A instance of B
판별 결과를 boolean 타입 변수에 저장하거나 if문에 판별하는 문장 직접 사용 가능
< 기본 문법 >
if(A instanc of B) { // A는 참조변수 B는 클래스명 // 형변환 가능 } else { // 형변환 불가능 }
public static void main(String[] args) {
Smartphone sp = new Smartphone("아이폰14", "010-1234-5678", "애플");
sp.call();
sp.sms();
sp.kakaoTalk();
// 상속 받았기 때문에 슈퍼클래스의 메서드까지 호출 가능
if(sp instanceof Handphone) {
System.out.println("sp는 Handphone이다");
Handphone p = sp; // 업캐스팅
p.call();
p.sms();
// 업캐스팅하면 공통된 메서드만 사용 가능하기 때문에
// 더 이상 카카오톡은 이용할 수 없음
} else {
System.out.println("sp는 Handphone이 아니다")
System.out.println("형변환 불가!")
}
Handphone hp = new Smartphone("아이폰14", "010-1234-5678", "애플"); // 업캐스팅
if(hp instance of Smartphone) {
System.out.println("hp는 smartphone이다");
// 다운캐스팅 가능
Smartphone sp2 = (Smartphone)hp;
// 대신 자동 형변환이 안되기 때문에 강제형변환 필요!
sp2.call();
sp2.sms();
sp2.kakaoTalk();
// 다운캐스팅 해서 다시 3개의 메서드 모두 사용 가능
} else {
System.out.println("hp는 smartphone이 아니다");
System.out.println("형변환 불가능");
}
}
class Handphone {
String modelName;
String number;
public Handphone(String modelName, String number) {
super();
this.modelName = modelName;
this.number = number;
}
public void call() {
System.out.println("전화 가능");
}
public void sms() {
System.out.println("문자 가능");
}
}
class Smartphone extends Handphone {
String osName;
public Smart(String modelName, String number, String osName) {
super(modelName, number);
this.osName = osName;
}
public void kakaoTalk() {
System.out.println("카톡 가능");
}
}
상속 관계에서 업캐스팅 후 메서드 호출 시 컴파일 단계에서 실행 대상과 실제 실행 단계에서의 실행 대상이 달라진 것
참조 변수 타입과 무관하게 실제 인스턴스의 메서드를 실행하게 됨
⭐ 입력
public static void main(String[] args) {
Parent p = new Parent();
p.parentPrn();
Child c = new Child();
c.childPrn();
c.parentPrn();
System.out.println("====================================");
p = c; // 업캐스팅
p.parentPrn();
// 업캐스팅 했기 때문에 더이상 childPrn() 호출 불가
}
class Parent {
public void parentPrn() {
System.out.println("슈퍼클래스의 parentPrn()");
}
}
class Child extends Parent {
public void childPrn() {
System.out.println("서브클래스의 childPrn()");
}
public void parentPrn() { // 오버라이딩 했음
System.out.println("서브클래스에서 오버라이딩된 parentPrn()");
}
}
📌 출력
슈퍼클래스의 parentPrn()
서브클래스의 childPrn()
서브클래스에서 오버라이딩된 parentPrn()
====================================
서브클래스에서 오버라이딩된 parentPrn()
업캐스팅 후 parentPrn()
메서드를 호출하면 Child
클래스에서 오버라이딩된 parentPrn()
메서드가 호출됨
메서드 코드를 작성하는 시점(컴파일 시점)에는 Parent
클래스의 parentPrn()
메서드를 호출하는 코드이지만 실행 시점에는 Child
클래스에서 오버라이딩된 parentPrn()
메서드가 호출됨
=> 컴파일 단계에서의 실행 대상과 실행 단계에서 실행 대상이 다름