자바의 다형성 (Polymorphism)

Kongsub·2020년 7월 28일
0

JAVA

목록 보기
13/15
post-thumbnail

다형성 (Polymorphism)

: 객체지향개념에서 다형성이란 "여러 가지 형태를 가질 수 있는 능력"을 의미하며 상속과 깊은 관계를 가지고 있다.
구체적으로 말하자면, 조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 하였다는 것이다.

EX Code

class Tv {
	boolean power;
    int channel;
    
    void power() { power = !power; }
    void channelUp() { ++channel; }
    void channelDown() { --channel; }
}

class CaptionTv extends Tv {
	boolean caption;
    void displayCaption(String test) {
    	if(caption)
        	System.out.println(text);
    }
}

class CaptionTvTest {
	public static void main(String args[]){
    	CaptionTv ctv = new CaptionTv();
        ctv.channel = 10;
        ctv.channelUp();
        System.out.pringln(cvt.channel);
        ctv.caption = true;
        ctv.displayCaption("Hello World");
    }
}

Tv클래스와 CaptionTv클래스의 관계를 다음그림처럼 나타낼 수 있다.

클래스Tv와 CaptionTv는 서로 상속관계에 있고 이 두 클래스의 인스턴스를 생성하고 사용하기 위해서는 아래와 같이 할 수 있다.

Tv t = new Tv();
CaptionTv c = new CaptionTv();

위의 코드 처럼 인스턴스의 타입과 참조변수의 타입이 일치하는 것이 일반적이지만, 상속관계에 있을 경우, 아래와 같이 조상 클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조하는 것이 가능하다.

CaptionTv c = new CaptionTv();
Tv t = new CaptionTv();

하지만 위의 같은 경우, 인스턴스 타입이 CaptionTv이기는 하지만, 참조 변수 t로는 CaptionTv인스턴스의 모든 멤버를 사용할 수 없다.
정확하게 말하자면, Tv타입의 참조변수로는 CaptionTv인스턴스 중에서 Tv클래스의 멤버들(상속받은 멤버포함)만 사용이 가능하다.
c와 t는 같은 타입의 인스턴스지만 참조변수 타입에 따라 사용할 수 있는 멤버의 개수가 달라진다.

주의 : 아래와 같은 코드를 작성할 경우에는 컴파일 에러로 이어진다.
자식 클래스의 참조변수로 부모 클래스의 인스턴스를 참조할 수 없다.
왜냐하면 자식 클래스가 호출할 수 있는 멤버의 개수가 부모 클래스보다 많거나 같기 때문이다.

CaptionTv c = new Tv();

1. 다형성 정리

  1. 조상 타입의 참조변수로 자손타입의 인스턴스를 참조할 수 있다.
  2. 반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수 없다.

2. 참조변수 형변환

참조변수 형변환은 서로 상속관계가 있는 클래스 사이에서만 가능하기 때문에 형변환에 2가지 경우가 있다.

첫번째 경우 - UP - Casting
: 자손타입 -> 조상타입 - 형변환 생략 가능

두번째 경우 - Down - Casting
: 자손타입 <- 조상타입 - 형변환 생략 불가

형변환은 참조변수의 타입을 변환하는 것이다.
따라서 인스턴스를 변환하는 것은 아니기 때문에 참조변수의 형변환은 인스턴스에 아무런 영향을 끼치지 않는다.

그렇다면 참조변수의 형변환을 왜 할까?
참조변수의 형변환을 통해, 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 범위(메서드 개수 & 인스턴스 개수)를 조절하는 것이다.

3. instanceof연산자

: 참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 사용하는 연산자이다.
이 연산자의 왼쪽에는 참조변수를 오른쪽에는 타입(클래스명)이 위치한다. 그리고 return값으로는 boolean값인 ture or false를 리턴한다.

instanceof연산자를 통해 true값을 얻었다면, 참조변수가 검사한 타입으로 형변환이 가능하다는 것을 뜻한다.
code EX

class Car {
	String color;
    int door;
    void drive(){
    	System.out.println("drive,  Brrrrr!");
    }
    void strop(){
    	System.out.println("stop!!!!!");
    }
}

class FireEngein extends Car {
	void water() {
    	System.out.println("Water!!!");
    }
}
class Ambulance extends Car {
	void siren() {
    	System.out.println("siren~~~~~~~");
    }
}

위의 코드는 FireEngein클래스와 Ambulance클래스가 Car클래스를 상속 받고 있다.
Car클래스의 참조변수로 각각 FireEngein클래스와 Ambulance클래스의 인스턴스를 참조할 수 있다.
따라서 Car 클래스가 가르키는 클래스가 무엇인지 확인헤야할 필요가 있다.

void doWork(Car c){	
	if (c instanceof FireEngeine) {
    	FireEngine fe = (FireEngien)c;
        fe.water();
        ...
    } else if(c instanceof Ambulance) {
    	Ambulance a = (Ambulance) c;
    	a.siren ();
    		...
    }
    	...
}

instanceof 연산자를 이용해서 참조변수 c가 가리키고 있는 인스턴스 타입을 확인하고, 적절히 형변환을 하여 작업을 한다.

4. 참조변수와 인스턴스 연결

조상 타입의 참조변수와 자손 타입의 참조변수의 차이점은 사용할 수 있는 멤버의 개수이다.
하지만 여기서 더 알아야할 점이 존재한다.

그것은 바로 조상 클래스에 선언된 멤버변수와 같은 이름의 인스턴스 변수를 자손 클래스에 중복으로 정의했을 때,
조상 타입의 참조변수로는 자손 인스턴스를 참조하는 경우와 자손타입의 참조 변수로 자손 인스턴스를 참조하는 경우는 서로 다른 결과를 가져온다.

완전 당연한 이야기이다.

예제 코드를 살펴보다
EX code

class BindingTest{
	public static void main(String[] args) {
    	Parent p = new Child();
        Child c = new Child();
        
        System.out.println("p.x = " + p.x);
        p.method();
        
        System.out.println("c.x = " + c.x);
        c.method();
   }
}

class Parent {
	int x = 100;
    
    void method() {
    	System.out.println("Parent Method");
    }
}

class Child extends Parent {
	int x = 200;
    
    void method() {
    	System.out.println("Child Method");
    }
}

실행결과
p.x = 100
Parnet Method
c.x = 200
Child Method

다음 예제코드를 보면, 참조변수 Parnet로 선언한 p의 결과는 Parent 클래스에 있는 멤버 변수와 함수가 호출된 것을 확인 할 수 있고, 참조변수 Child로 선언한 c의 결과는 Child 클래스에 있는 멤버 변수와 오버라이딩된 함수가 호출된 것을 확인 할 수 있다.

5. 활용

조상 타입의 참조변수로 자손 타입의 객체를 참조하는 것이 가능하기 때문에, 다음과 같이 코드를 표현할 수 있다.

Car c1 = new FireEngein();
Car c2 = new Ambulance();

//이를 아래 코드로 바꿀 수 있다. 

Car c[] = new Car[2];
c[0] = new FireEngein();
c[1] = new Ambulance();

다음 코드를 보면, FireEngein과 Ambulance의 서로 다른 객체를 하나의 배열로 관리 하는 것을 보여준다.
즉, 다형성을 이용하면 여러 종류의 객체를 배열로 다룰 수 있다.

profile
심은대로 거둔다

0개의 댓글