강좌 Course 1. Part 4. ch4 요약
다형성은 객체지향 프로그래밍에서 가장 중요한 요소로 꼽힌다. 상속을 기반으로 하기 때문에 기본적으로 상속의 장점인 코드 중복 최소화, 유지보수 용이성, 확장성을 가지고 있다.
다시 상속을 공부할 때 사용했던 Dog, Cat, Animal 클래스를 보자. upcasting을 사용하여 Animal 변수에 Dog, Cat 객체를 저장하고 eat()을 실행하면, Dog, Cat이 각각 재정의한 eat()이 출력된다. 이것이 다형성이다.
다시 말해, 다형성이란 상위 클래스가 동일한 메시지로 하위 클래스를 서로 다르게 동작시키는 객체지향 이론이다.
public class PolyTest {
public static void main(String[] args) {
Animal x = new Dog(); // upcasting
x.eat(); // "Eat like a dog"
x = new Cat();
x.eat(); // "Eat like a cat" --> "Message Polymorphism"
((Cat)x).night(); // downcasting
}
}
다형성의 전제조건은 다음과 같다.
1. 상속관계
2. Override(재정의)
3. Upcasting(업캐스팅)
4. 동적바인딩 사용
+정적 바인딩은 컴파일시점에 사용될 동작이 정해진다. 오버로딩에 정적 바인딩에 사용되며, 프로그램 실행 속도와 무관하다.
+ 동적 바인딩은 실행속도와 관계가 있다.
인수, 인자는 메서드에서 받는 매개변수나 파라미터를 가리킨다. 다형성 인수는 인수로 부모 클래스를 받는 것을 말한다. 부모 클래스를 받으면 인수로 자식 클래스가 와도 오류없이 처리가 되기 때문에, 하나의 타입으로 여러 타입을 받을 수 있다.
다형성 인수를 사용하면 하위 클래스마다 다른 타입을 받아 여러번 같은 동작을 작성할 필요가 없어 코드 중복을 최소화할 수 있고 유지보수가 용이해진다.
public class PolyMethodTest {
public static void main(String[] args) {
Dog d = new Dog();
display(d);
Cat c = new Cat();
display(c);
}
// 다형성 사용 x display()
public static void display(Dog d){
d.eat();
}
public static void display(Cat c){
c.eat();
}
}
다형성을 활용하지 않는다면 위의 예시에서는 타입마다 개별적으로 display()를 하나씩, 총 2개를 만들어야 한다. 하지만 다형성을 활용하면,
public class PolyMethodTest {
public static void main(String[] args) {
Dog d = new Dog();
display(d);
Cat c = new Cat();
display(c);
}
// 다형성 사용 o display()
public static void display(Animal a){
a.eat();
}
}
하나의 타입의 인수로 여러 타입을 받을 수 있다.
그런데 Animal로 받기 때문에 night()는 실행할 수 없다. display()에 다운캐스팅을 사용해 ((Cat)a).night()를 추가할 수도 있지만, 이 경우에는 Dog로 인수를 받은 경우에도 night()를 실행할 수 있게 되어 바람직하지 않다. 이런 경우에는 Cat 타입으로 받은 경우에만 실행하도록 if문을 작성해주면 된다.
타입을 검사할 때는 instanceof를 사용한다.
((인수) instanceof (타입))
public static void display(Animal a){
a.eat();
// Cat타입인 경우에만 night() 실행
if (a instanceof Cat){
((Cat)a).night();
}
}
배열은 동일한 자료형이 가능하지만 ,부모타입의 배열은 자식타입을 저장할 수 있다. 부모타입의 배열에 서로 다른 자식 타입을 저장하는 것을 다형성 배열이라고 한다.
public class PolyArrayTest {
public static void main(String[] args) {
Dog d = new Dog();
Cat c = new Cat();
Animal a[] = new Animal[2];
a[0] = d;
a[1] = c;
for(int i=0;i<a.length;i++){
a[i].eat();
}
}
}
여기서도 마찬가지로 night()를 그냥 실행할 수 없다. night()를 실행하려면 instanceof로 Cat타입인지 확인하고, if문으로 실행하도록 만들면 된다.
위의 for문을 메서드로 분리하여 작성할 수도 있다.
public static void display(Animal[] a){
for(int i=0;i<a.length;i++){
a[i].eat();
// Cat타입인 경우에만 night() 실행
if(a[i] instanceof Cat){
((Cat)a[i]).night();
}
}
}