다형성이란??? 


여러 가지 형태를 가질 수 있는 능력!

자바에선 한 타입의 참조변수여러 타입의 인스턴스를 참조할 수 있는 것!


보통 A a = new A(); 이렇게 참조변수와 인스턴스의 타입이 같은 경우를 흔히 보았을 것이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package test;
 
public class Poly {
    public static void main(String args[]) {
        
        Human a = new Human();
        Human b = new Programer();
    }
}
 
class Human {
    String name;
    void eat(){
    }
}
 
class Programer extends Human {
    void coading(){
    }
}
 
cs


 

 하지만 위 예제를 보면 Human 타입의 참조변수가 Human의 인스턴스와 Programer의 인스턴스를 참조한다.

한가지 타입의 참조변수로 여러 타입의 인스턴스를 담았다!


다만 아무 타입이 아무 타입의 인스턴스를 참조할 수 있는 것은 아니다


 

 조건

 

 조상타입의 참조변수로 자손타입의 인스턴스를 참조할 수 있다. 하지만 역은 성립하지 않는다!!

 즉 참조변수가 사용할 수 있는 멤버의 개수는 인스턴스의 멤버 개수보다 같거나 적어야 하는 것이다.

 Human b = new Programer(); 의 경우 Programer의 인스턴스를 참조중이라 해도 Human타입의 참조 변수기 때문에 Human이 가지고있는 name과 eat() 멤버만 사용 가능하다 

 즉 참조 변수의 타입에 따라서 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 수가 달라지게 된다!


Programer b = new Human(); 만약 이런식으로 한다면 Programer의 참조변수이므로 coading()을 쓸 수 있지만 Human인스턴스를 참조중이고 Human은 coading()을 가지고 있지 않으므로 문제가 발생한다. 그래서 위에 언급한 조건이 있는 것이다.



 참조변수의 형변환


참조변수도 기본형과 같이 형변환이 가능하다. 다만 상속관계에 있는 클래스사이에서만 가능하다. (간접적인 상속관계 즉 조상의 조상 같은경우도 가능)


byte b = 4;           업캐스팅 형변환 생략

int i = b; 


int i = 30;             다운캐스팅 형변환 생략 불가

byte b = (byte)i;  


 기본형 변수의 형변환이다. 보면 더 작은 자료형인 byte에서 더 큰 int로의 형변환은 생략됨을 볼 수 있다.

 마찬가지로? 자손타입에서 조상타입으로의 형변환은 생략이 가능(업캐스팅) 하지만 반대는(다운캐스팅) 생략이 불가능하다.


내가 이해하기엔 자손클래스가 부모클래스보다 큰? (더 많은 멤버를 가지고 있으니 당연한거 아닌가?)개념이라 처음에 무지 헷갈렸다.


 아까보았던 Human c = new Programer(); 이 예제도  Human c = (Human)new Programer(); 사실 저형변환이 생략된 형태이다.(업캐스팅)


다운캐스팅


1. Programer p = (Programer)new Human();    그럼 이렇게 형변환을 명시해주면 되는 것일까??

답은 아니다! 컴파일은 되지만 런타임 에러가 뜨고만다.


2. Human h = new Programer();

   Programer p = (Programer)h;

 

 이렇게 해주어야 한다. 왜 일까?

형변환은 단순히 참조변수의 타입을 변환해주는 것이지 인스턴스에는 영향을 끼지지 않는다. 무슨말이냐면!

Human이 Programer의 타입으로 바뀐다해도 Human은 coading()을 가지는 것은 아니다.!!!

그래서 결국 참조변수 p가 다룰 수 있는 멤버의 개수가 Human인스턴스보다 더 많아 불가능한 것이다 (계속 같은말..) 


 2.의 경우 첫번째 줄을 보면 h는 Programer의 인스턴스를 참조하지만 Programer의 멤버를 Human이 가지고 있는 멤버만큼만 이용이 가능하다

 Programer인스턴스를 참조하는 h의 정보를 형변환으로 타입을 맞추어 p에 제공하면 Programer의 타입으로 Programer를 참조하는 것이다. 당연히 되고 모든 멤버를 이용가능해 진다.


 처음 이 개념을 공부 했을때 왜 구지 이런 짓을 하지? 라는 생각이 딱 들었다. 방법은 알아도 활용도가 와닿지 않았다.

하지만 다운캐스팅을 활용을하는 예제를 밑에 두었으니 차차 다음내용도 보자!




매개변수의 다형성


동호회를 만들어 클래스로 관리하려한다. Club클래스를 만들고 클럽 멤버로 프로그래머가 올수도 학생이 올수도 있다. 그러면


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
class Human {
    String name;
    void eat(){
    }
}
 
class Programer extends Human {
    void coading(){
    }
}
class Student extends Human{
    void study() {
        
    }
}
 
class Club {
    static int memberNumber=-1;
    String clubName= "java";
    String [] members = new String[10];
    
    void member(Programer p) {
        members[++memberNumber] = p.name;
    }
    void member(Student s) {
        members[++memberNumber] = s.name;
    }
 
}
cs


 이렇게 Club이라는 클래스에 member라는 메소드를 여러번 오버로딩 해주어야 할까?? 여기서는 2개의 상황만 나열했지만  실제로 가입하려는 사람들은 더 다양한 직업을 가졌을 것이다.

 하지만 매개변수의 다형성을 적용해보자. Programer와 Student 모두 공통의 조상인 Human을 가지고 있다.


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
class Human {
    String name;
    void eat(){
    }
}
 
class Programer extends Human {
    void coading(){
    }
}
class Student extends Human{
    void study() {
        
    }
}
 
class Club {
    static int memberNumber=-1;
    String [] members = new String[10];
    String ClubName;
    Club(String a) {
        ClubName=a;
    }
    void member(Human h) {
        members[++memberNumber] = h.name;
    }
    
}
cs

 

 이렇게 하나의 메소드로도 표현할 수 있게 된다. (24번줄)


 멤버의 특성이 이름만 있을까? 위 코드에는 그렇지만 실제로는 나이도있고 성별도있고 등등 더 많을 것이다.

그렇다면 members라는 배열을 객체의 배열로 만들어 하나의 배열에 객체정보를 담으면 되지 않을까?



여러 종류의 객체를 하나의 배열로 다루기


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Club {
    static int memberNumber=-1;
    Human [] members = new Human[10];
    String ClubName;
    Club(String a) {
        ClubName=a;
    }
    void member(Human h) {
        members[++memberNumber] = h;
    }
    
    void getMemberInfo(int number) {
        System.out.println("회원이름 :"+ members[number].name);
        System.out.println("회원나이 :"+ members[number].age);
    }
    
}
cs


하나의 배열의 여러 종류의 객체를 담아 더 간단하게 코딩이 가능해진다. (3번줄)


1
2
3
4
5
6
7
8
9
10
11
public class Poly {
    public static void main(String args[]) {
        
        Club A = new Club("java");
        Programer Programer1 = new Programer();
        Student Student1 = new Student();
        A.member(Programer1);
        A.member(Student1);
        
    }
}
cs

이렇게 클럽A의 member배열에 여러종류의 객체가 담긴다. (7,8번줄)



 하지만 위에 코딩에서는 클럽멤버의 수를 정해 10으로 정해놓았다. 물론 동호회 인원을 정해놓고 더 안받는 경우도 있겠지만 그렇지 않는 경우는 어떻게 해야할까?  이 블로그에서 다루진 않았지만 가변배열중에 하나인 Vector 클래스를 사용하면 된다.

Vector클래스는 내부적으로 Object타입의 배열을 가지고 있어서 이배열에 어떤 객체든 추가가 가능하다.


다운캐스팅 예제 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Club {
 
    Vector members = new Vector();
    String ClubName;
    Club(String a) {
        ClubName=a;
    }
    void member(Human h) {
        members.add(h);
    }
    void getMemberInfo(int number) {
        Human h =(Human)members.get(number);
        
        System.out.println("회원이름 :"+ h.name);
        System.out.println("회원나이 :"+ h.age);
    }
    
}
cs


 자 3번줄을 보면 더이상 멤버의 수가 제한되지 않게 members를 Vector로 만들어 주었고 member의 추가도 9번줄 Vector의 메소드인 .add()로 해주었다.

 여기서 알고가야할 점은 Vector의 add() 메소드는 객체를 Object타입의 변수로 저장한다는 것이다. 물론 오늘 배운 핵심인 조상의 참조변수로 자식의 인스턴스를 담을 수 있다. 라는 점을 어긋나지 않아 가능하다 Object는 모든 클래스의 조상이니.. 


 하지만 Object는 name이나 age같은 변수를 가지고 있지 않아 사용할 수 없다.


그래서!


 12줄을 보면 Vector의 있는 정보를 꺼내 Object의 자식중 하나인 Human으로 다운캐스팅 하는 것을 볼 수 있다!!! 그럼다시 Human은 name,age를 가지고 있기 때문에 사용이 가능해진다. 


 이렇게 예제를 통해 다형성, 참조변수의 업캐스팅,다운캐스팅, 매개변수의 다형성, 여러 종류의 객체를 하나의 배열로 다루는 것을 알아보았다.

 나는 이부분을 공부하면서 처음에 이해가 너무 어려웠고 특히 다운캐스팅 같은경우는 왜 저런짓을 하는지 이해가 안갔다. 하지만 이런 예제를 통해 이해가 되어 무리를 해서라도 만들어 보았다.. 


참고

자바의 정석 남궁 성 지음

+ Recent posts