본문 바로가기
Java/Java 기초문법

[JAVA 기초] 자바 다형성(polymorphism)

by dev수니 2021. 3. 12.
반응형

 

 

다형성은 객체지향언어에서 객체의 자료형을 변경하는 것을 말한다.

 

 


 

 

 1  다형성이란?

 

 

다형성은 사전적 의미로는 '여러 형태 가지는 성질'을 뜻합니다. 객체지향 개념에서 다형성은 '한 가지 타입이 여러 가지 형태의 인스턴스를 가질 수 있다'라는 의미이다. 특히, 부모타입 변수에는 모든 자식 인스턴스들이 대입될 수 있는데, 이점은 객체지향언어의 중요한 특징 중 하나이다.

 

 

지금까지는 인스턴스를 생성하고 참조변수에 할당할 때 인스턴스와 참조변수의 클래스 타입을 동일하게 작성했다.

 

A obj = new A();

 

그러나 다형성의 정의에 따라 조상 클래스 타입의 참조변수로 자손 클래스 타입의 객체를 참조할 수도 있다.

 

A obj = new B();
( 클래스 B가 A를 상속할 때)

 

클래스 B의 데이터 형이 클래스 A이다. 클래스 B는 클래스 A를 상속하고 있다. 이런 경우에 클래스 B는 클래스 A를 데이터 형으로 삼을 수 있다.

참조변수 obj 하나로 A타입의 인스턴스를 참조할 수도, B타입의 인스턴스를 참조할 수도 있는 데 이것이 다형성이다.

 

 

 


 

 

 2  참조변수와 인스턴스 간의 관계

 

 

 

다형성을 이용할 때 중요한 것 중의 하나는 조상 클래스 타입의 참조변수로 자손 인스턴스를 참조하는 것은 가능하지만 그 반대의 경우는 에러가 발생한다는 것이다.

 

 

 

● Child i = new Parents();

 

 

자손 클래스는 부모 클래스를 상속하면서 부모의 멤버들에 자신의 멤버들을 더해 확장된다. Child 클래스가 부모의 멤버 A, B, C를 상속받고 클래스 내부에 D라는 멤버를 선언하였다고 가정하자. 그다음 Child 타입 참조변수로 부모 클래스의 인스턴스를 참조하려고 할때, Child 타입 참조변수 i는 멤버 A, B, C, D를 사용할 수 있지만 Parents 타입 인스턴스는 자손 클래스의 고유한 멤버 D가 없다. 즉 참조변수 i는 존재하지 않는 멤버를 사용할 수도 있기 때문에 에러가 발생하게 된다.

 

 

부모 클래스의 참조변수에서 자손 클래스의 인스턴스를 참조할 때 주의할 점은 부모 클래스의 참조변수로 참조가 불가능한 멤버가 존재한다는 점이다. 

 

 

● Parents p = new Child();

 

 

Parents 타입의 참조변수로 Child 타입의 인스턴스를 참조한다. 그러나 참조변수 p는 멤버 A, B, C만 참조할 수 있기 때문에 D라는 멤버는 참조면 당연히 에러가 발생할 것이다.

 

 

 

 

 

 

 

 


 

 

 3  다형성의 활용

 

1. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package java_practice;
 
class A {
    void methodA() {
        System.out.println("methodA");
    }
}
class B extends A{
    void methodB() {
        System.out.println("methodB");
    }
}
public class Polymorphism {
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        A obj = new B();    // 데이터 타입이 부모클래스 A인 참조변수 obj 인스턴트 B 생성
        obj.methodA();
//      obj.methodB();    // 에러발생
    }
}
cs
결과
method A

 

위와 같이 줄17에 데이터 타입이 부모클래스 A인 참조변수 obj 인스턴트 B를 생성해주었다.

B는 자식클래스인 B 인스턴스이지만 참조변수 obj의 데이터타입은 부모클래스인 A이기 때문에 실질적으로 A클래스의 행세를 한다. 따라서 줄19에서 methodB()를 호출했을 때 에러가 발생하게 된다.

 

 

그리고 다음과 같이 메서드 methodA()를 자식 클래스B에서 오버라이드 해줬다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package java_practice;
 
class A {
    void methodA() {
        System.out.println("methodA");
    }
}
class B extends A{
    void methodA() {    // @Override
        System.out.println("methodA_B");
    }
    void methodB() {
        System.out.println("methodB");
    }
}
public class Polymorphism {
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        A obj = new B();
        obj.methodA();
//      obj.methodB();    // 에러발생
    }
}
cs
결과
methodA_B

 

클래스 B의 methodA()가 상위클래스인 클래스 A의 메소드를 오버라이딩 했을 경우에는 인스턴스 obj의 데이터타입이 부모클래스인 A더라도 우선순위에 의해 클래스 B의 메소드가 호출된다.

따라서 줄21에서 methodA()를 호출할 경우 줄9의 클래스 B내의 methodA()가 실행되게 되는 것이다.

 

 

 

 

 

 

 

 

 

 

2. 동물원을 가정하자. 동물객체를 만들고, 동물객체를 상속한 사자 객체를 만들고, 사육사 객체를 만들었다.

 

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
package Zoo;
 
 
class Animal {
    void breath() { System.out.println("숨쉰다."); }
}
class Lion extends Animal {
    public String toString() {
        return "사자 객체";
    }
}
class ZooKeeper {
    void feed(Lion obj) {
        System.out.println(obj + "에게 먹이주기.");
    }
}
 
public class Polymorphism_Animal {
    public static void main(String[] args) {
        Lion obj1 = new Lion();
        ZooKeeper obj2 = new ZooKeeper();
        
        obj1.breath();
        obj2.feed(obj1);
    }
}
cs
결과
숨쉰다. 사자 객체에게 먹이주기.
 
 

위와 같이 줄20, 21에서 Lion 인스턴스와 ZooKeeper 인스턴스를 만들어 주었다.

그리고 줄23에서 obj1(Lion클래스) 의 breath()를 호출하였다. Lion클래스는 Animal클래스를 상속한 것이기 때문에 breath메서드로 가지고 있으므로 "숨쉰다."가 실행된다.

그리고 줄24에서 obj2(Zookeeper클래스) 의 feed를 호출해 주었다. feed를 호출하는 동시에 매개변수를 obj1으로 넣어주었고 이는 줄13에서 Zookeeper 클래스의 feed() 메서드 안의 Lion타입의 obj로 값을 받게 된다. 따라서 feed() 메서드 내의 sysout이 실행 될때 obj는 Lion 클래스의 내용이 출력되게 된다.

 

 

그리고 다음은 토끼와 원숭이를 추가한 코드이다.

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package Zoo;
 
class Animal {
    void breath() { System.out.println("숨쉰다."); }
}
class Lion extends Animal {
    public String toString() {
        return "사자 객체";
    }
}
class Rabbit extends Animal {
    public String toString() {
        return "토끼 객체";
    }
}
class Monkey extends Animal {
    public String toString() {
        return "원숭이 객체";
    }
}
class ZooKeeper {
    /*
    void feed(Lion obj) {
        System.out.println(obj + "에게 고기주기.");
    }
    void feed(Rabbit obj) {
        System.out.println(obj + "에게 고기주기.");
    }
    void feed(Monkey obj) {
        System.out.println(obj + "에게 고기주기.");
    }
    */
    void feed(Animal obj) {
        System.out.println(obj + "에게 고기주기.");
    }
}
public class Poly_Zoo {
    public static void main(String[] args) {
        Lion obj1 = new Lion();
        Rabbit obj2 = new Rabbit();
        Monkey obj3 = new Monkey();
        
        ZooKeeper zk = new ZooKeeper();
        zk.feed(obj1);
        zk.feed(obj2);
        zk.feed(obj3);
    }
}
결과cs
결과
사자 객체에게 고기주기.
토끼 객체에게 고기주기.
원숭이 객체에게 고기주기.

 

사자를 포함한 토끼와 원숭이 객체도 Animal클래스를 상속받았으며 줄39, 40, 41에서 인스턴스를 각각 생성시켜주었다.

그리고 각 동물들의 feed 메서드를 출력하기 위해 Zookeeper 클래스 안에서 주석처리한 내용을 보면 각 동물마다 파라미터로 클래스의 타입을 obj로 받은 것을 볼 수 있다. 하지만 각 동물들의 클래스는 모두 Animal클래스를 상속받을 것이기 때문에 위와 같이 코드를 중복할 필요없이 줄33과 같이 작성하면 된다.

반응형

댓글