어떤 객체(인스턴스)를 다른 타입으로 변환
참조형 데이터타입간의 형변환
반드시 상속 관계가 전제되어야 함
(상속 관계가 아닌 타입끼리의 형변환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() 메서드가 호출됨
=> 컴파일 단계에서의 실행 대상과 실행 단계에서 실행 대상이 다름