티스토리 뷰

Java

[JAVA] Casting(upCasting, doenCasting)

Hwan'ss 2019. 7. 23. 21:34

1. upCasting(업캐스팅)

  • 참조형 캐스팅의 경우 사용하는 경우다.
  • 기본적으로 캐스팅은 서로 관련없는 데이터끼리는 변환되지 않는다.
  • 참조형에서 참조형 데이터가 서로 관련이 있다는 것은 무슨 의미냐면 가. 상속 관계가 맺어진 경우, 나. 인터페이스로 인해 확장이 된 경우를 말한다.

가. 상속 관계가 맺어진 경우

  • class Parent가 있고 class Child extends Parent 클래스가 있다고 가정하자.
  • Child는 Parent 클래스를 상속 받으므로 Child 클래스가 Parent 클래스 보다 가지고 있는 데이터 양이 무조건 많다. 왜냐하면 Child 클래스는 적어도 Parent 클래스의 데이터를 가지고 있기 때문이다.

(1). Parent parent = new Child();

  • parent 변수는 Parent 자료형 데이터 모두를 원한다.
  • new Child(); 인스턴스는 Parent의 데이터를 모두 가지고 있을까? 라고 묻는 다면 가지고 있다. 자식 클래스인 Child 클래스는 부모인 Parent의 클래스를 모두 상속을 받았기 때문이다.
  • 따라서 위의 식은 성립한다.
  • 하지만 캐스팅의 개념상 '(2)' 과 같이 형변환 기호를 붙여주어야 한다.

(2). Parent parent = (Parent)new Child();

  • (1)과 다르게 형변환 기호를 붙여 주었지만 기본적으로 변수가 원하는 정보를 상속을 받았기 때문에 다 가지고 있다. 그렇기 때문에 컴파일러가 형변환 기호를 붙이지 않더라고 다형성 측면으로 넘어가는 것이다.
  • 위와 같은 캐스팅을 'upCasting(업캐스팅)' 이라고 부른다.

2. downCasting(다운캐스팅)

  • 업캐스팅과 다르게 Child의 변수가 Parent()를 받는 다고 생각을 하면 어떨까?
  • Parent parent = new Child(); <업캐스팅> -> Child child = new Parent(); <다운캐스팅>

가. Child child = new Parent();

  • 관련있는 데이터 끼리는 캐스팅이 가능하다고 했지만 '가' 와 같은 경우는 성립이 되지 않는다.
  • 왜냐하면 앞서 포스팅에서 덧붙여 설명했던 개념인, 변수가 원하는 정보를 다 채워줘야하는 원칙에 어긋나기 때문이다.
  • Child 클래스는 Parent 클래스를 상속받았기 때문에 Parent 클래스보다는 Child 클래스가 더욱 많은 데이터를 가졌을 것이다.
  • 즉, Child 변수가 원하는 정보는 Child 클래스의 데이터 전부를 원하는데 Parent 인스턴스는 Parent 데이터만 가지고 있을 뿐, Child의 데이터를 가지지 않기 때문이다. (빨간줄이 그이면서 컴파일 에러)

나. Child child = (Child) new Parent();

  • '나'와 같이 선언을 한다면 빨간줄은 사라지고 컴파일은 되지만 런타임 오류가 발생한다.
  • 왜냐하면, 컴파일러에게 프로그래머가 형변환을 함으로써, 일단 데이터를 맞게 넣어준것 처럼 보여준다. 컴파일러는 문법이 맞다고 생각하여 넘기는 것이다. 하지만 프로그램이 실제로 동작할 때, new Parent(); 인스턴스는 Child 형 데이터로 바꾸지 못한다는 것을 깨닫고, 런타임 오류를 뽑으며 프로그램이 종료된다.
  • 왜 런타임 오류가 발생할까? 그 이유는 JVM은 new Parent(); 인스턴스를 Child 데이터로 형변환 하려 했지만, Child 데이터가 무엇인지 모르기 때문이다. 조금 더 구체적인 이유는 Child 데이터는 만드는 프로그래머마다 성질이 다를 것인데, 그것을 JVM 추리하지 못하기 때문이다. 
  • 그냥 Casting(캐스팅)에서 기본 자료형 끼리는 추리가 가능하기 때문에, 알아서 알맞은 데이터 크기로 변환하여 넣어주지만 위와 같이 참조현 데이터를 캐스팅 할때는 속성, 성질이 정해져 있지 않은 참조현 데이터는 JVM이 알길이 없다.

다. Parent parent = new Child();

     Child child = (child) parent;

  • 다운캐스팅은 일반적으로 성립하지 않는다. 하지만 다운캐스팅이 성립하는 경우가 존재한다. 바로 '다' 의 경우에 다운캐스팅은 성립한다.
  • 성립되는 이유는? parent 변수는 Parent클래스 형태의 변수지만, 태생이 Child 인스턴스인 데이터를 넣어주었다. 그러한 정보를 가지고 있는 parent 변수를 다시 Child 클래스 형태로 다운캐스팅 하였다. parent 변수 상태는 Parent 클래스형 상태이지만, 다운캐스팅을 해주는 경우( (Child)parent ) 태생이 Child 클래스 형이므로, JVM이 parent 변수를 태생 정보인 Child 클래스 데이터 형으로 다운캐스팅을 해줄 수 있는 것이다.
  • 결과적으로, 다운캐스팅은 보통 성립하지 않는 문법이지만, 위와같이 업캐스팅이 선행된 경우, 다운캐스팅이 성립되는 경우가 존재한다.

3. upCasting(업캐스팅) 예제

가. 예제1

  • 과일 클래스
class Apple {
	void func01() {
		System.out.println("<부>함수 1번 call");
	}
	void func03() {
		System.out.println("<부>함수 3번 call");
	}
}

class Orenge extends Apple {
	void func02() {
		System.out.println("<오렌지_자>함수 2번 call");
	}
	void func03() {
		System.out.println("<오렌지_자>함수 3번 call");
	}
}

class Banana extends Apple {
	void func03() {
		System.out.println("<바나나_자>함수 3번 call");
	}
}

class Kiwe extends Apple {
	void func03() {
		System.out.println("<키위_자>함수 3번 call");
	}
}

class Dog {
	void func01(Apple a) {
		a.func03();
	}
	void func02(Banana b) {
		b.func03();
	}
	void func03(Kiwe k) {
		k.func03();
	}
}
  • main()
public class Aapple {
	public static void main(String[] args) {
		// 부->자 <업캐스팅>
		Apple a1 = new Orenge(); 
		a1.func01(); // func02()사용 불가, 메모리는 존재하지만 문법 사용 불가
		a1.func03(); // 문법상 자식을 사용하지는 못하지만 메모리에는 이미 존재하고 있기 때문에 오버라이딩 된 자식 함수가 출력됨.
	}
}
  • 실행결과

'가'(예제1) 실행결과

나. 예제2

  • 예제1과 클래스는 동일하다.
  • main()
public class Aapple {
	public static void main(String[] args) {
		
		Random rand = new Random();
		int num = rand.nextInt(3);
		
		//위 스위치문과 똑같은 문장이다.
		//다형성을 사용하지 않으면 이 문장이 성립 할 수 없다.
		Apple[] a4 = new Apple[3];	// Apple 객체 3개를 만들 수 있는 배열 변수 선언(아직 객체 생성되지 않음, 배열만 생성)
		a4[0] = new Orenge(); // 부->자<업캐스팅>
		a4[1] = new Banana(); // 부->자<업캐스팅>
		a4[2] = new Kiwe(); // 부->자<업캐스팅>
		
		a4[num].func03();
		
		Dog d = new Dog();
		// 인수의 전달은 일종의 대입연산이다.
		// 부모(Apple)가 받아서 메소드 오버라이딩(다형성)을 이용한다.
		// 여러개의 함수를 선언하여 사용 할 필요가 없다.
		d.func01(new Orenge()); // Orenge 객체를 인수로 사용
		d.func01(new Banana());
		d.func01(new Kiwe());
	}
}
  • 실행결과

'나'(예제2) 실행 결과

 

#이것은 꼭 알고 가자.

Parent parent = new Child(); <업캐스팅> 의 경우에 

parent는 문법적으로 사용을 할 수 있는 것이고 Child는 메모리에 할당이 된것으로 생각을 하면된다.

자식이 부모의 데이터를 모두 가지고 있기 때문에 가능하다라고 해석하면 이해하기 편하다.

 

Child child = new Parent(); <다운캐스팅> 의 경우에는

자식의 문법을 모두 사용하고자 하지만 메모리에는 부모 데이터만 할당되어 있기 때문에 위와 같은 문법을 사용 할 수가 없다.

 

[2019.07.12(금) Test02 project]

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함