BackEnd/Java

0929 JAVA - Polymorphism 다형성

jeoniee 2022. 9. 29. 12:02
728x90
반응형
polymorphism;
Ex

 

 

 

다형성(polymorphism)이란?

다형성(polymorphism)이란 하나의 객체가 여러 가지 타입을 가질 수 있는 것을 의미

=레퍼런스 형변환

 

자바에서는 이러한 다형성을 부모 클래스 타입의 참조 변수로 자식 클래스 타입의 인스턴스를 참조할 수 있도록 함.

다형성은 상속, 추상화와 더불어 객체 지향 프로그래밍을 구성하는 중요한 특징 중 하나이다.

 

 

레퍼런스 형변환(참조형변환)의 장점

코드가 간략하게 줄어들 수 있다! 

 

 

 

 

 

참조형 데이터타입 간의 변환 

 

반드시 상속 관계가 전제되어야한다. 

상속 관계가 아닌 타입끼리의 형변환은 에러가 발생한다. 

 

 

 

슈퍼클래스 타입 서브클래스 타입 
업캐스팅 (Up Casting) 다운캐스팅 (Down Casting)
묵시적 형변환 (자바가 알아서 해주는 변환) 명시적 형변환 (내가 강제로 변환하는 것) 
ex) (String) 어저구 저쩌구~

 

 

 

 

 

 

 

1.업캐스팅 (Up Casting)

슈퍼클래스 타입 레퍼런스로 서브클래스 인스턴스를 참조하는 것.

 

서브클래스의 인스턴스를 슈퍼클래스 타입으로 변환 하는 것이다. 

컴파일러에 의해 자동으로 형변환이 일어나는 것, 별도의 형변환 연산자를 사용하지 않아도 된다.

 

 

 

		//Parent 클래스의 인스턴스 (p) 생성
		Parent p = new Parent();
		
		p.parentPrn();
		// Parent 타입으로 p 참조변수로 접근 가능한 메서드 : 1개 
//		p.childPrn();
		// 서브클래스에서 직접 정의한 메서드는 호출 불가능. 
		
		System.out.println("---------------------------------------------------");
		
		//서브클래스 child 인스턴스 c 생성
		Child c = new Child();
		c.parentPrn(); //슈퍼클래스에서 상속받은 메서드 
		c.childPrn();  //서브클래스에서 직접 정의한 메서드
		
	}

}
class Parent{
	// Parent 클래스의 멤버  : 1개  (자신의 멤버)
	public void parentPrn() {
		System.out.println("슈퍼클래스의 parentPrn()");
	}
	
}

class Child extends Parent {
	// Child 클래스의 멤버 : 2개 (자신의 멤버 & 상속받은 멤버)
	public void childPrn() {
		System.out.println("서브클래스의 childPrn()");
		

	
	}
}

 

참조 가능한 영역의 축소 

 

슈퍼클래스로부터 상속 된 멤버만 접근 가능!

 

서브클래스 인스턴스의 멤버 중 공통 항목을 제외한 나머지에 대한 포기 선언을 하는 것과 같다.

=> 대신, 하나의 슈퍼클래스 타입으로 여러 서브클래스 인스턴스를 참조할 수 있다!

 

업캐스팅(Up Casting)은 마치 자동형변환같은 것이다

 

Parent p=new Child();

부모가 알고있는 것만 쓸 수 있기 때문에 참조 가능한 영역이 축소된다.

 

Parent p;
p = c;

p.parentPrn();
p.childPrn();

 

서브클래스에서 직접 정의한 메서드는 접근 불가하다.


슈퍼클래스 타입으로 업캐스팅 후에는 상속된 (공통) 메서드만 호출 가능하다.


즉, 슈퍼클래스 타입으로 서브클래스 인스턴스를 참조하게 되면 참조 영역에 대한 축소로 인해 서브클래스 메서드가 보이지 않게 된다. 

 

 

 

 

2. 다운캐스팅 (Down Casting)

 

서브클래스 타입 레퍼런스로 슈퍼클래스의 인스턴스를 참조하는 것

=슈퍼클래스의 인스턴스를 서브클래스 타입으로 변환하는 것

 

참조 가능한 영역이 확대됨

컴파일러에 의해 자동 형변환이 일어나지 않음

=>강제 형변환(명시적 형변환) 필수

=> 존재하지 않는 영역에 대한 참조 위험성 때문에 명시적 형변환 후에도 오류가 발생할 수 있다!

 

 

슈퍼클래스 Parent 타입 인스턴스 p2 생성
Parent p2 = new Parent();

슈퍼클래스 p2로 접근 가능한 메서드는 1개 
p2.parentPrn();

 

서브 클래스 Child 타입 변수 c2 선언
Child c2;

서브 클래스 타입 변수 c2 에 슈퍼 클래스 타입 인스턴스 전달해보기
c2 = p2;   (Type Mismatch!!!!!!!!) 

 

 

컴파일 에러난다!!!!  왼쪽과 오른쪽의 타입이 달라서 형변환 필요. 

왼쪽이 자식이고, 오른쪽이 부모이면 강제 형변환 필요하다. (왼쪽이 부모이면 자바가 업캐스팅 해줌)

 

자동 형변환 불가...강제로 형변환 해줄 것.

 

(오류발생시점 o)

c2 = (Child) p2;

 

byte b에 int i 를 넣으려면, byte b = (byte) int i ; 이렇게 선언하는 거랑 똑같은 원리 

 

다운 캐스팅 한 것이다.

 

그런데 실행시키면 에러가 발생한다.

 

문법적 오류는 해결 했으나,   실행 시점에서 논리적 오류가 발생함.

 

 java.lang.ClassCastException: Parent cannot cast to Child 

 

자식 클래스 타입으로 부모 생성자를 호출하면 발생하는 오류.

부모의 범위가 더 크기 때문에, 자식에 담을 수 없다. 

 

Child타입 변수 c2는 접근 가능한 메서드 2개

 

 

 

(오류발생시점 x) 

c2.parentPrn();    실제 Parent 인스턴스에 존재하는 메서드 c2.childPrn();   실제 Parent 인스턴스에 존재하지 않는 메서드 

 

존재하지 않는 영역에 대한 참조 위험성 때문에 다운캐스팅이 불가능. 메서드 호출 시점이 아닌 다운 캐스팅 시점에서 오류가 발생한다. 

 

 

 

 

예외


다운캐스팅(Down Casting)은 자바에서는 기본적으로 허용이 안되지만,  딱 한 가지 경우만 가능하다.

이전에 이미 업캐스팅(Up Casting)된 인스턴스를

                                다시 다운캐스팅(Down Casting)하는 경우에만 허용한다. 

 

 

 

 

 



child 타입 인스턴스를 parent 타입 변수 p3에 전달 (업캐스팅)

Parent p3 = new Child();

 

묵시적 형변환 발생 => 참조 영역 축소됐음. 접근 가능한 메서드 = 1개 

 

p3.parentPrn();

 

상속된 공통 메서드 접근 가능, 

 

p3.childPrn();

상속 안된 (서브클래스에서 정의한) 메서드 호출 불가

 

 

업 - > 다운

업캐스팅 된 parent 타입 p3의 인스턴스(주소값)을 Child 타입 변수 c3에 전달 

Child c3 = (Child)p3;

 

Child 에 p3 을 넣었다. (강제 형변환)

 

오류안남!

다운캐스팅에 의해 참조 영역이 확대 되므로 접근 가능한 메서드 : 2개

 

 

 

상속되지 않은 메서드 (서브클래스) 

c3.childPrn();

 

상속받은 메서드 

c3.parentPrn();

 



다운캐스팅 후에도 아무런 문제없이 인스턴스 사용이 가능하다. 

 

 

결론

 

다운캐스팅 후에도 실행 시 오류가 발생할 수 있으니 인스턴스의 클래스타입과 참조하는 레퍼런스 변수의 상속 관계를 고려해서 다운캐스팅을 해야 한다.

 

이전에 이미 업캐스팅 된 인스턴스(레퍼런스)를 다시 다운캐스팅하는 경우에만 안전(그 외는 인정x) 

 

 

 

 

 

 

 

 

 

문제


레퍼런스 형변환 연습 

서브클래스 타입 (스마트폰) 인스턴스 생성
public static void main(String[] args) {
		//레퍼런스 형변환 연습 
		//서브클래스 타입 (스마트폰) 인스턴스 생성
		스마트폰 내폰 = new 스마트폰();
		
		//서브클래스 타입으로 참조 가능한 메서드 : 3개 
		내폰.전화();
		내폰.문자();
		내폰.카톡();
		
		//슈퍼클래스 타입(핸드폰) 인스턴스 생성 
		핸드폰 어머니폰 = new 핸드폰();

		//슈퍼클래스 타입으로 참조 가능한 메서드 : 2개
		어머니폰.전화();
		어머니폰.문자();
		
		System.out.println("--------------------------------");
		
//		업캐스팅 예)
//		내가 쓰던 스마트폰 (내폰)을 어머니껟 ㅡ릴 경우
//		스마트폰 (서브) -> 핸드폰 (슈퍼) 타입으로 변환하는 경우
		
		어머니폰 = 내폰;  // 자동형변환이 일어났다.
		
//		어머니가 사용 가능한 기능 : 2개 
		어머니폰.전화(); //핸드폰에서 사용 가능한 기능 
		어머니폰.문자(); //핸드폰에서 사용 가능한 기능 
		//어머니폰.카톡(); //스마트폰에서 사용 가능한 기능(안보임) = 사용 불가 
		//스마트폰을 핸드폰타입으로 참조할 경우 기능이 축소됨 
		
		//또 다른 스마트폰(동생폰)을 어머니께 드릴 경우 
		//=> 스마트폰 (서브) - > 핸드폰 (슈퍼) 타입으로 변환 
		스마트폰 동생폰 = new 스마트폰();
		어머니폰 = 동생폰;
		어머니폰.전화();
		어머니폰.문자();
		//어머니폰.카톡();
		//스마트폰을 핸드폰타입으로 참조할 경우 기능이 축소됨 
		
		//어머니폰(핸드폰 타입)으로 여러 스마트폰을 사용 할 수 있다.
		
		System.out.println("=====================================");
		
		//다운캐스팅 성공적으로 수행되는 예)
		어머니폰 = new 스마트폰(); //스마트폰 -> 핸드폰 업캐스팅
		어머니폰.전화();
		어머니폰.문자();
		
		내폰 = (스마트폰)어머니폰; //다운캐스팅 수행 
		//참조 가능한 영역이 확대 발생
		// -> 사용 가능한 기능이 3가지 확대됨 
		// -> 이전에 이미 스마트폰 -> 핸드폰으로 업캐스팅 한 뒤에  ▲
		// -> 다시 스마트폰으로 다운 캐스팅 한 것이다. ▼
		
		내폰.문자();
		내폰.카톡();
		내폰.전화();
		
		
		
	}

}

class 전화기{
	public void 전화() {
		System.out.println("전화 걸기!");
	}
}

class 핸드폰 extends 전화기{
	public void 문자() {
		System.out.println("문자 전송!");
	}
}

class 스마트폰 extends 핸드폰 {
	public void 카톡() {
		System.out.println("카톡 전송!");
	}

 

 

 

 

728x90
반응형