JAVA/다형성(Polymorphism)

자바(JAVA) 다형성(Polymorphism) 상속 업캐스팅(UpCasting) 다운캐스팅(DownCasting)

justgodoit 2024. 7. 11. 00:43

상속 구조 클래스들 간 형변환 

1. UpCasting

자식 타입 > 부모 타입

자동형변환

ex)

부모 = 자식객체;

자식.부모메소드();

 

2. DownCasting

부모타입 > 자식타입

강제형변환

ex)

((자식)부모).자식메소드();

 

 

다형성 

부모타입으로부터 파생된 여러 타입의 자식 객체들을 부모타입 하나로 다룰 수 있는 기술이다.

 

다형성 적용의 목적

 

장점

1. 다수의 자식 객체들을 하나로 관리할 수 있어서 용이하다.

2. 메소드 정의 시 매개변수로 부모타입을 기술하게 되면 메소드 갯수를 줄일 수 있다.

 

 => Object 클래스에 equals메소드의 매개변수가 Object로 설정되어있다. (다형성적용)

 

equals

학생객체.equals(학생객체);

도서객체.equals(도서객체);

제품객체.equals(제품객체);

 

자바에서 적용되는 경우가 많기 때문에 알아두어야한다.

부모타입인 오브젝트 메소드로 되는 경우도 많아 다운캐스팅을 해야할 때도 있을 것이다.

 

public static void main(String[] args) {

	// = 대입연산자 왼쪽, 오른쪽 자료형이 같이야됨!
		
	System.out.println("1. 부모타입 레퍼런스로 부모객체를 다루는 경우");

	Parent p1 = new Parent();
	p1.printParent();
	// p1 레퍼런스로 Parent에만 접근가능
}

 

 

자식타입 레퍼런스로 부모타입과 자식타입 레퍼런스로 둘다 접근이 가능하다.

public static void main(String[] args) {

	// = 대입연산자 왼쪽, 오른쪽 자료형이 같이야됨!

	System.out.println("2. 자식타입 레퍼런스로 자식객체를 다루는 경우");
		
	Child1 c1 = new Child1();
	c1.printChild1();
	c1.printParent();
}

 

자동형 변환

public static void main(String[] args) {
	
	System.out.println("3. 부모타입 레퍼런스로 자식객체를 다루는 경우");
		
	Parent p2 = new Child1(); // 자동형변환
    
}​

 

 

Child1 타입

Child1 타입은 Child1 타입만 담을 수 있다.

    Child1[] arr1 = new Child1[2];
    arr1[0] = new Child1(1, 2, 4);
    arr1[1] = new Child1(2, 3, 5);

 

Child2 타입

Child2 타입은 Child1 타입만 담을 수 있다.

    Child2[] arr2 = new Child2[2];
    arr2[0] = new Child2(1, 2, 4);
    arr2[1] = new Child2(2, 3, 5);

 

배열 객체를 각각 타입에 맞게만 담을 수 있다.

 

하지만 다형성이라는 것을 적용했을 경우에 부모 자식 타입 간에 아래와 같이 진행할 수 있다.

 

다형성 적용 후

부모 타입의 배열을 작성한다.

	// 다형성 적용 후 Child1 객체 2개, Child2 객체 2개 관리
	Parent[] arr = new Parent[4]; // {Parent1, Parent2, Parent3, Parent4}
	arr[0] = new Child1(1, 2, 4);
	arr[1] = new Child2(4, 2, 1);
	arr[2] = new Child2(2, 3, 1);
	arr[3] = new Child1(2, 3, 5);

 

하나의 부모 타입으로 여러 자식 객체들을 코드길이를 줄이며 편리하게 관리할 수 있다.

		((Child1)arr[0]).printChild1();
		((Child2)arr[1]).printChild2();
		((Child2)arr[2]).printChild2();
		((Child1)arr[3]).printChild1();

실수로 다른 타입으로 형변환을 할 경우 오류가 발생한다.

 

아래와 같이 작성 시 아래와 같은 오류 구문이 발생한다.

	((Child1)arr[0]).printChild1();
	((Child2)arr[1]).printChild2();
	((Child2)arr[2]).printChild2();
	((Child1)arr[3]).printChild1();
		
	((Child2)arr[3]).printChild1();

 

 

((Child2)arr[3]).printChild1(); // 부적절한 형변환시 ClassCastException 발생

 

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
The method printChild1() is undefined for the type Child2

at cohttp://m.br.chap01_poly.run.Run.main(Run.java:55)

 

형변환 조건문

어떤 타입의 자식객체가 담겨있는지 알 경우

((자식)arr[i].자식메소드());

for(int i=0; i<arr.length; i++) {
	if(i == 0 || i == 3) {
		((Child1)arr[i]).printChild1();
	}else {
		((Child2)arr[i]).printChild2();
	}
}

 

 

그럼 어떤 타입의 자식인지 부모인지 어떤 형이 들어있는지 모르는 경우 조건식을 어떻게 작성하나요?

 

비교대상을 다음과 같이 작성 후 참조할 클래스타입을 입력해서 비교해볼 수 있다.

 

ex)

for (int i = 0; i < arr.length; i++) {
	if (arr[i] instanceof Child1) {
		((Child1) arr[i]).printChild1();
	} else {
		((Child2) arr[i]).printChild2();
	}
}

비교대상 : arr[i]

연산자 : instanceof (현재 레퍼런스가 실질적으로 어떤 클래스 타입을 참조하는지 확인할 때 사용한다.

클래스 : Child1

if(arr[i] instanceof Child1 

 

동적바인딩

프로그램 실행 전 컴파일 시점에는 정적바인딩에 의해 부모클래스의 메소드를 가리키지만 프로그램이 실행되는(런타임 시점)에는 실제로 자식클래스에 오버라이딩 된 메소드가 실행된다. 아래와 같이 볼 수 있다.

 

.java(소스코드)▶컴파일▶.class(byte코드) ▶실행 ▶ 프로그램실행

 

Run.java (정적바인딩)

System.out.println("============오버라이딩 적용=================");
		
	for(int i=0; i<arr.length; i++) {
		arr[i].print(); 

}

 

Parent.java (부모메소드)

public void print() {
	System.out.println("나 부모야");
}

 

Child1.java (자식메소드)

@Override
public void print() {
	System.out.println("나 첫번째 자식이야");
}

 

Run.java의 public void print의 프린트문을 출력하는데 왜 Parent.java "나 부모야"라는 내용만 있는데 실제로 프로그램이 실행되면 오버라이딩 된 내용이 나오냐? 동적바인딩이라고 한다.

 

향상된 for문 적용