본문 바로가기

프로그래밍/Java

[Java] 업캐스팅, 다운캐스팅

 캐스팅이란

 타입의 변환을 말하며 형변환이라고도 한다. 자바에서 상속관계에 있는 클래스는 캐스팅, 즉 형변환이 가능하다. 자식 클래스가 부모 클래스 타입으로 형변환되는 것을 업캐스팅이라 하며, 반대로 부모 클래스가 자식 클래스 타입으로 캐스팅 되는 것을 다운 캐스팅이라 한다. 

 

업캐스팅이란

 부모클래스를 상속하는 자식클래스는 부모의 모든 멤버를 사용할 수 있게된다. 때문에 자식 클래스는 부모클래스로 취급받을 수 있게된다. 그렇듯 자식클래스를 부모클래스 타입으로 형변환 하는 것을 업캐스팅이라 한다.

 

 예를들어, 생물(부모클래스)이라는 큰 범주안에 사람(자식클래스)가 포함된다. 이때 '사람은 사람이라' 라고 하지 않고 '사람은 생물이다' 라고 하는 것이 업캐스팅이다.

 

업캐스팅의 특징

 - 부모클래스 타입으로 업캐스팅 될 경우 자식 객체는 부모클래스가 가지고 있는 멤버만 사용할 수 있다.

 - 자식클래스가 부모클래스로 업캐스팅되는 경우 명시적인 형변환을 선언하지 않아도 자동으로 형변환이 된다.

 

업캐스팅을 하는 이유

 클래스들의 공통적인 부분을 부모클래스로 묶어, 간편화 시키자는데 목적이 있다. 예를들어 철수, 짱구, 맹구, 훈이, 유리를 운동장으로 집합 시키기위해서는 '철수야 운동장으로 가', '짱구야 운동장으로 가',  '...' '유리야 운동장으로 가' 라고 여러번의 동작을 수행해야 할 것이다. 하지만 이들을 해바라기반이라는 부모 클래스로 업캐스팅시키면 '해바라기반 운동장으로 가'라는 동작으로 단순화 시킬수 있게된다.

 

 

 

다운캐스팅이란

 부모타입으로 업캐스팅 된 자식클래스를 다시 자신(자식클래스)의 형태로 되돌리는 것을 말한다. 그렇기 때문에 다운캐스팅이 일어나기 위해서는 먼저 업캐스팅이 일어나야한다.

 

다운캐스팅의 특징

 - 업캐스팅이 먼저 선행되어야 한다.

 - 명시적으로 타입을 지정해주어야한다.

 

다운캐스팅을 하는 이유

업캐스팅으로 인하여 부모클래스 타입의 참조변수로 자식객체의 모든 멤버에 접근하는 것이 불가능 하여
다운 캐스팅이라는 것이 나왔다.

 

업캐스팅/다운캐스팅 예제

// 업캐스팅이란


class Parent {

	public void parentPrn() {
		System.out.println("부모클래스 : parentPrn메소드");

	}
	
} // Parent class

class Child extends Parent {

	public void childPrn() {
		System.out.println("자식 클래스 : childPrn메소드");
	}

} // Child class 

public class Test94 {

	public static void main(String[] args) {

		// 일반 기본 자료형
		int a = 10; // 4바이트 크기의 a변수 메모리에 저장한 정수 10(작은 데이터)
		long b = 20; // 8바이트 크기의 b변수 메모리에 저장한 정수 20(큰 데이터)
		
		// 8바이트 크기 b변수 메모리에 4바이트 크기 a변수 메모리에 저장된 정수 10(작은데이터)
		// 를 저장하려고 할 때 개발자가 직접 강제로 int데이터 10을 long데이터 10l로 형태를 변환할 수 있다.
		
		b = (long)a;
		
		b = a;
		// 자동으로 int 데이터 10을 long데이터 10l로 데이터의 형태가 자동으로 변환되어
		// 저장된다.
		// 요약 : 자동 형변환이 일어난다.
		
		
		// 참조 자료형의 형 변환(객체의 주소를 참조하는 자료 형태의 형 변환)
		
		// 자식 객체 생성
		Child child = new Child();
		// Child 자식클래스타입의 참조변수 child로 생성한 new Child() 객체 메모리에 접근하여
		// 호출할 수 있는 메소드는 2개 (부모꺼 하나 자식꺼 하나)
		
		child.parentPrn();
		child.childPrn();
		
		// 업캐스팅이란
		// 1. 부모 클래스 타입의 참조변수 선언
		Parent p;
		
		// 2. 부모 크래스 타입의 참조변수에 자식객체의 주소를 저장시 부모클래스 타입으로 형변환해서 자식 객체의 주소 저장
		// p = (Parent)child; 
		p = child; // 업캐스팅이 일어남
		
		// 위 2.에서 업캐스팅 후에 Parent p참조변수로 호출할 수 있는 메소드는
		// 부모클래스 내부에 있는 parentPrn()메소드만 호출가능 하므로
		// 업캐스팅을 함으로써 부모클래스 타입의 참조변수 하나에 여러 자식 객체의 주소를 지정해서
		// 사용할 수 있기 때문에, 참조변수를 낭비하여 여러개를 만들어 놓지 않아도 된다는 장점은 있지만
		// 부모클래스에 존재하지 않는 자식 클래스 내부의 메소드는 호출 불가능 하다는 단점도 있다.
		
		// 부모클래스 타입의 참조변수에 자식객체의 주소를 저장시 부모클래스 타입으로 형변환 해서 자식 객체의 주소 저장
		Parent child2 = new Child();
		
		
		Parent parent = new Parent();
		
	}

}
// 다운캐스팅
class Parent1 {

	public void parentPrn() {
		System.out.println("부모클래스 : parentPrn메소드");

	}
	
} // Parent class

class Child1 extends Parent1 {

	public void childPrn() {
		System.out.println("자식 클래스 : childPrn메소드");
	}

} // Child class 

public class Test95 {

	public static void main(String[] args) {

	//	- 다운 캐스팅이란
	//	자식 클래스 타입의 참조변수에 저장된 자식객체의 주소를 다시 저장하는 형태
		// 부모클래스 타입의 참조변수를 선언하고 부모객체 생성하여 주소 저장
		Parent1 p1 = new Parent1();
		
		// 자식 클래스 타입의 참조변수만 선언
		Child1 c1;
		// -> Child1은 Parent1을 상속받고 있기 때문에 2개의 멤버를 가지고 있다.
		
		// 다운 캐스팅
		// c1 = p1; // 자동형 변환 안된 경우 에러 발생
		
		
		c1 = (Child1)p1;
		// p1보다 c1의 멤버수가 더 많기 때문에 강제 형변환(다운 캐스팅)이 필요하다.
		
		
		
		Child1 c2 = (Child1)new Parent1();
		// 잘못된 다운 캐스팅의 예
		// 이유 : 업캐스팅 하지않고 다운캐스팅을 했기 때문에
		
	}

}
 
class Parent2 {

	public void parentPrn() {
		System.out.println("부모클래스 : parentPrn메소드");

	}
	
} // Parent1 class


class Child2 extends Parent2 {

	public void childPrn() {
		System.out.println("자식 클래스 : childPrn메소드");
	}

} // Child1 class

public class Test96 {

	public static void main(String[] args) {

		/*
		 * 업캐스팅 및 다운캐스팅의 예 
		 */
		
		// 업캐스팅 요약
		// 방법1. 자식클래스를 이용해 자식객체 생성 후
		// 부모클래스타입의 참조변수에 대입 (업캐스팅)
		
		// 방법2. 부모클래스 타입의 참조변수에 저장된 자식객체의 주소로 자식객체 메모리를 참조할 수 있다.
		// 자식객체 메모리를 참조할 수 있다라는 뜻
		
		// 업캐스팅
		// 부모클래스타입   참조변수    =    자식객체
		     Parent2           p2       =    new Child2();
		
	    // 업캐스팅의 단점
		// 업캐스팅을 함으로써 부모클래스 타입의 참조변수로 사용할 수 있는
		// 자식객체(new Child2())의 멤버는 부모클래스 내부에 있는 멤버만 사용할 수 있다.
		     p2.parentPrn(); // 부모클래스(Parent2)에 있는 멤버만 호출 가능
		     // p2.childPrn(); // 부모클래스에 없기때문에 호출 불가능
		
		// 업캐스팅의 단점(자식메소드의 멤버까지 사용하게)을 해결하기 위한 방법
		
		     
		// 다운캐스팅을 하자
		// 다운 캐스팅의 순서
		// 순서1. 자식클래스 타입의 참조 변수 선언
		
		Child2 c2;
		// 순서2. 자식클래스 타입으로 선언한 참조변수에 부모클래스타입의 참조변수에 저장된
		//		 자식객체의 주소값을 대입한다(강제 다운캐스팅)
		
		c2 = (Child2)p2;
		// 다운 캐스팅
		
		// 다운캐스팅으로 인해 Child2클래스타입의 참조변수 c2로
		// childPrn()메소드와 ParentPrn()메소드 모두 호출 가능하게됐다.
		
		c2.childPrn();
		c2.parentPrn();
		
		
		//-------------------------------------------------------
		
		/* 다운 캐스팅의 잘못된 예 */
		/* 생성한 부모객체를 자식클래스 타입의 참조변수에 저장하여 접근하려고 시도한 잘못된 예 */
		
		// 부모클래스 타입의 참조변수에 부모객체 생성하여 저장
		Parent2 p3 = new Parent2();
		
		// 다운캐스팅 시도
		// 순서1. 자식클래스 타입의 참조변수 선언
		Child2 c3;
		// 순서2. 자식클래스 타입으로 선언한 참조변수에 부모클래스 타입의 참조변수에 저장된
		// 자식객체의 주소값을 대입함(강제로 다운캐스팅 시켜야 한다.)
		c3 = (Child2)p3;
		// 다운 캐스팅함
		
		// 잘못된 다운캐스팅 이후 Child2클래스 타입의 c3참조변수로 각각의 메소드들을 호출 해보자
		
		c3.parentPrn();
		c3.childPrn();
		
		// Test96.class파일을 JVM이 실행 시 예외(에러)가 발생 한다.
		
		
		/*
		
			기본 데이터(int, double...)와는 달리 클래스 데이터 간의 형변환(캐스팅)은
			두 단계(컴파일 단계, 실행 단계)에 걸쳐서 JVM이 체크한다.
			컴파일상의 에러를 막기 위해서는 다운캐스팅을 명시적으로 해주면 되지만
			Test96.class파일을 실행시키는 단계에서 한번 더 에러를 체크한다.
			캐스팅 연산자에 의해 명시적인 다운캐스팅(형변환)을 c3 = (Child2)p3; 하였지만
			참조변수 c3가 참조하는 객체의 클래스 타입과 일치하지 않는 경우에는
			실행 단계에서 ClasscastException이라는 에러 사항이 발생하게 된다.
			
			결론 : 업캐스팅으로 인해 자식객체들을 참조하는 부모클래스 타입의 참조변수를
				   다시 다운캐스팅 해서 자식객체를 생성할 때의 자식클래스 타입과 참조변수의 클래스 타입을
				   동일하게 하는 것에 한해서만 다운캐스팅을 허용한다.
			
			정리
			다운 캐스팅이란
			- 자식클래스 타입으로 형변환 하는 것.
			- 멤버를 참조하는 영역이 확대되게 하는 것을 의미한다.
			- 컴파일러에 의해 자동으로 형변환 되지 않는다. (= 프로그래머에 의해 강제로 형 변환이 필요하다.)
			- 객체의 클래스타입과 참조하는 참조변수의 상속관계를 생각해서 명시적으로 형변환을 해줘야한다.
			- 이전에 이미 업캐스팅이 된 참조변수 값을 다운캐스팅 하는 경우에만 안전하다.
			
			중요포인트 : 상속관계가 아닌 두 클래스를 이용해서 다운캐스팅 시도한 잘못된 예
			*/
		
//			Child2 c4;
//			String str = new String();
			
			// c4 = (Child2)str; // 상속관계가 아니므로 에러 발생
			
		}

}

'프로그래밍 > Java' 카테고리의 다른 글

[Java]제네릭  (0) 2021.04.28
[Java]Collection  (0) 2021.04.28
[Java]접근 지정자  (0) 2021.04.06
[Java]Getter와 Setter  (0) 2021.04.02
[Java]상속  (0) 2021.04.02