다형성이란?
객체지향에서 다형성이란 '여러 가지 형태를 가질 수 있는 능력'을 뜻한다.
자바는 한 타입의 참조변수로 여러 타입의 객체를 참조할 수 있도록 함으로써 다형성을 프로그램적으로 구현하였다.
구체적으로 말하면 조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 하였다
class Tv {
boolean power;
int channel;
void power() { power = !power; }
void channelUp() { ++channel; }
void channelDown() { --channel; }
}
class CaptionTv extends Tv {
String text;
void caption() {}
}
클래스 Tv를 상속받는 클래스 CaptionTv가 있다고 하자, 우리가 보통 참조변수를 사용할 때는
Tv t = new Tv();
CaptionTv ct = new CaptionTv();
이런 식으로 인스턴스 타입과 일치하는 타입의 참조변수만을 사용했다.
하지만..
Tv t = new CaptionTv();
CaptionTv ct = new CaptionTv();
이렇게도 참조가 가능하다.
둘 다 CaptionTv의 인스턴스를 생성한것 같지만 참조변수 t로는 CaptionTv의 멤버를 사용할수 없다.
클래스 | 멤버 | Tv t = new CaptionTv() | CaptionTv ct = new CaptionTv() |
Tv | boolean power | 사용 가능 | 사용 가능 |
Tv | int channel | 사용 가능 | 사용 가능 |
Tv | void power() | 사용 가능 | 사용 가능 |
Tv | void channelUp() | 사용 가능 | 사용 가능 |
Tv | void channelDown() | 사용 가능 | 사용 가능 |
CaptionTv | String text | X | 사용 가능 |
CaptionTv | void caption() | X | 사용 가능 |
둘다 같은 타입의 CaptionTv 인스턴스지만 참조변수 타입에 따라 사용할 수 있는 멤버가 달라진다.
또 반대로 자손타입의 참조변수가 조상타입의 인스턴스를 참조하는 것은 불가능하다
CaptionTv ct = new Tv(); //불가능! err 발생!
조상타입의 참조변수로 자손타입의 인스턴스를 참조할 수 없다.
반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수는 없다.
참조변수의 형변환
참조변수도 기본형 변수와 같이 서로 상속관계에 있는 클래스 사이라면 형변환이 가능하다.
자손타입의 참조변수를 조상타입으로 조상타입의 참조변수를 자손타입으로 형변환이 가능하고
조상의 조상 자손의 자손 모두 가능하다.
기본형 변수도 작은 자료형에서 큰 자료형의 형변환은 문법 생략이 가능하듯이
참조형도 자손타입의 참조변수를 조상타입의 참조변수로 형변환 할 때는
형변환을 생략할 수 있다.
캐스트 연산자는 괄호() 안에 변환하고자 하는 타입의 이름(클래스명)을 적어주면 된다.
자손타입 → 조상타입(Up - casting) : 형변환 생략가능
조상타입 → 자손타입(Down - casting) : 형변환 생략불가
instanceof연산자
참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 instanceof연산자를 사용한다.
주로 조건문에 사용되는데 instanceof의 왼쪽에는 참조변수를 오른쪽에는 타입(클래스명)이 피연산자로
위치한다. 그리고 연산의 결과로 boolean값인 true와 false 중의 하나를 반환한다.
void doWork(Car c)
if (c Instanceof FireEngine) {
FireEngine fe = (FireEngine)c;
fe.water();
...
} else if(c instanceof Ambulance) {
Ambulance a = (Ambulance)c;
a.siren();
...
}
...
}
위 코드는 Car타입의 참조변수 c를 매개변수로 하는 메서드인데
이 메서드가 실행될때 매개변수로 Car클래스나 그 자손 클래스의 인스턴스를 넘겨 받고
매개변수 c가 어떤 인스턴스를 참조하는지 확인 한뒤
FireEngine을 참조하고 있으면 소방차의 메서드를 실행시키고
Ambulance를 참조하고 있다면 엠뷸런스의 메서드를 실행시킨다.
참조변수와 인스턴스의 연결
조상 타입의 참조변수와 자손 타입의 참조변수의 차이점은 멤버의 개수에 있다
하나 더 알아둘 내용이 있는데 조상 클래스에 선언된 멤버변수와 같은 이름으로
자손 클래스에 인스턴스 변수를 선언했을 때,
조상타입의 참조변수를 사용했을 때는 조상클래스에 선언된 멤버변수가 사용되고,
자손타입의 참조변수를 사용했을 때는 자손 클래스에 선언된 멤버변수가 사용된다.
매개변수의 다형성
class Product {
int price; // 제품의 가격
int bonusPoint; // 제품구매 시 제공하는 보너스점수
}
class Tv extends Product {}
class Computer extends Product {}
class Audio extends Product {}
class Buyer { // 고객, 물건을 사는 사람
int money = 1000; // 보유금액
int bonusPoint = 0; // 보너스점수
void buy(Tv t){
money = money - t.price;
bonusPoint = boustPoint + t.bonusPoint;
}
}
위의 코드를 보면 Product를 부모로 설정한 Tv, Computer, Audio 3개의 클래스가 있고
물건을 사기 위한 Buyer 클래스가 있다.
이때 물건을 사기 위한 메서드를 Buyer에 추가해 주어야 하는데
위에 코드대로라면 Tv, Computer, Audio 각각 구매 메서드를 만들어 주어야 한다
그러나 매개변수의 다형성을 이용하면 하나의 메서드로 간단히 처리 가능하다.
void buy(Product p){
money = money - p.price;
bonusPoint = boustPoint + p.bonusPoint;
}
이렇게 매개변수가 Product타입의 참조변수면 Product클래스의 자손타입이면 어느것이나 매개변수로
받아들일 수 있다는 뜻이다.
다른 제품 클래스를 추가할 때 Product클래스를 상속받기만 하면, buy(Product p) 메서드의
매개변수로 사용될수 있다.
여러 종류의 객체를 배열로 다루기
글을 쭉 읽었다면 조상타입의 참조변수로 자손타입의 객체를 참조하는 것이 가능하다는걸 알것이다.
Product가 Tv, Computer, Audio클래스의 조상일 때 다음과 같이 할수 있다
Product p[] = new Product[3];
p[0] = new Tv();
p[1] = new Computer();
p[2] = new Audio();
위의 코드처럼 조상이 공통 된다면 서로 다른 종류의 객체를 배여로 묶어 처리할 수 있다.
와우.. 정말 헷갈린다🤢
class Parent{
void method1(){}
void method2(){}
}
class Child extends Parent{
void method2(){}
void method3(){}
}
main{
Parent p = new Child();
p.method1(); // Parent의 메서드 실행
p.method2(); // Child의 오버라이딩된 메서드 실행
p.method3(); // 사용불가능
}
메서드2번은 Child의 메서드를 잘 쓰면서
3번은 못쓴다니. 뭔가 규칙이 있는데 내가 이해 못한건가?🤦♂️
규칙이 있다면 알려달라..😫
헷갈려도 다향성에 대해 알고 나니 기존에 적어둔 코드들이
거슬린다..
'여기도 합칠수 있고 저기도 합칠수 있고 얼핏 봐도 코드를 50줄 이상은 줄이겠는데?'
라는 생각이 드니 성공적인거 같다🤔
댓글