Java(7) 다형성
1. 다형성(Polymorphism)이란?
: 하나의 인스턴스가 여러가지 타입을 가질 수 있는것을 의미한다.
- 하나의 타입으로 여러 타입의 인스턴스를 처리할 수 있고, 하나의 메소드 호출로 객체 별로 각기 다른 방법으로 동작하게 할 수도 있다.
* cf. 객체지향 프로그래밍의 3대 특징: 1) 캡슐화 2) 상속 3) 다형성
2. 다형성의 장점
1) 여러 타입의 객체를 하나의 타입으로 관리 가능 -> 유지보수성 & 생산성 증가
Car[] carr = new Car[5];
carr[0] = new Sonata();
carr[1] = new Morning();
carr[2] = new Avante();
carr[3] = new Grandure();
carr[4] = new Porter();
for(Car car : carr) {
car.move();
}
// 서로 다른 종류의 차들이지만 Car type으로 관리 가능
2) 상속 기반 기술이기 때문에 상속관계에 있는 모든 객체는 동일한 메세지를 수신할 수 있다.
또, 동일한 메세지를 객체 별로 다르게 할 수도 있다. (다양한 기능 사용에 있어서 관리해야 하는 메세지 종류가 줄어든다.)
carr[0].moveSonata();
carr[1].moveMorning();
carr[2].moveAvante();
carr[3].moveGrandure();
carr[4].movePorter();
for(Car car : carr) {
car.move();
}
3) 확장성이 좋은 코드를 작성할 수 있다.
move(new Sonata());
move(new Morning());
/* 새로운 차량 추가 시 move 메소드를 더 만들지 않아도 된다. */
move(new Santafe());
public void move(Car car) {
car.move();
}
4) 결합도를 낮춰서 유지보수성을 증가시킬 수 있다.
// 결제수단을 현금만 사용하는 경우
public void pay(현금) {
현금.결제();
}
// 결제수단이 카드로 변경되는 경우
public void pay(카드) {
카드.결제();
}
// 카드결제 혹은 현금결제 둘 다 가능하도록 어느 한 쪽을 의존하지 않게 만듦
public void pay(결제수단) {
결제수단.결제();
}
3. 동적 바인딩
: 컴파일 당시에는 해당 타입의 메소드와 연결되어 있다가 런타임 시 실제 해당 인스턴스가 오버라이딩한 메소드로 바인딩이 바뀌어 동작하는 것
(1) 동적 바인딩의 성립 조건
- 상속 관계를 갖는 부모 자식 클래스에 오버라이딩 된 메소드를 호출해야 한다.
4. 업캐스팅과 다운캐스팅
- 상속 관계에 있지만 오버라이딩한 것이 아닌 후손 객체가 고유하게 갖는 확장된 기능 사용 위해선 실제 인스턴스의 타입으로 다운캐스팅을 해주어야 한다.
- 상위 타입 형변환(업캐스팅)은 묵시적으로 일어나지만 하위 타입 형변환(다운캐스팅)은 명시적으로 작성해야 한다.
5. instanceof 연산자
: 레퍼런스 변수가 실제로 어떤 클래스 타입의 인스턴스인지 확인하여 true/false를 반환
- 타입 형변환을 잘못하는 경우 ClassCastException이 발생하므로 안전한 형변환을 위해 instanceof 연산자를 이용할 수 있다.
If(레퍼런스 변수 instanceof 클래스 타입) {
// true일 때 처리할 내용, 해당 클래스 타입으로 down-casting할 것
}
<Animal, Rabbit, Tiger 클래스 정의>
public class Animal {
public void eat() {
System.out.println("동물이 먹이를 먹습니다.");
}
public void run() {
System.out.println("동물이 달려갑니다.");
}
public void cry() {
System.out.println("동물이 울음소리를 냅니다.");
}
}
public class Rabbit extends Animal{
@Override
public void eat() {
System.out.println("토끼는 풀을 뜯어 먹습니다.");
}
@Override
public void run() {
System.out.println("토끼가 깡총 깡총 달려갑니다.");
}
@Override
public void cry() {
System.out.println("토끼가 울음소리를 냅니다 끼익끼익");
}
public void jump() {
System.out.println("토끼가 점프합니다~! 점프~!");
}
}
public class Tiger extends Animal{
@Override
public void eat() {
System.out.println("호랑이가 고기를 뜯어 먹습니다.");
}
@Override
public void run() {
System.out.println("호랑이가 어슬렁 어슬렁 걸어갑니다.");
}
@Override
public void cry() {
System.out.println("호랑이가 어흥~");
}
public void bite() {
System.out.println("호랑이가 물어 뜯습니다 앙");
}
}
<main 메소드>
public class Application {
public static void main(String[] args) {
// Rabbit과 Tiger는 Animal이기도 하다.
// 부모 타입의 레퍼런스 변수로 자식 인스턴스의 주소 값 참조 가능 (업캐스팅)
Animal a1 = new Rabbit();
Animal a2 = new Tiger();
// 자식 타입의 레퍼런스 변수로 부모 타입의 인스턴스 주소 값 참조는 불가
// Rabbit r1 = new Animal();
// Tiger t1 = new Animal();
// 컴파일 당시에는 해당 타입의 메소드와 연결 되어 있다가 (정적 바인딩)
// 런타임 당시 실제 객체가 가진 오버라이딩 된 메소드로 바인딩이 바뀌어 동작 (동적 바인딩)
a1.cry();
a2.cry();
// 현재 레퍼런스 변수 타입은 Animal이기 때문에 Rabbit, Tiger의 메소드를 호출 불가
// a1.jump();
// a2.bite();
// 형변환을 통해 호출 (다운캐스팅)
((Rabbit)a1).jump();
((Tiger)a2).bite();
// 타입 형변환을 잘못 하는 경우 ClassCastException 발생
// ((Tiger)a1).bite();
// instanceof: 레퍼런스 변수가 참조하는 실제 인스턴스가 원하는 타입과 맞는지 비교
System.out.println("a1이 Rabbit 타입인지 확인 : " + (a1 instanceof Rabbit));
System.out.println("a1이 Tiger 타입인지 확인 : " + (a1 instanceof Tiger));
System.out.println("a2이 Rabbit 타입인지 확인 : " + (a2 instanceof Rabbit));
System.out.println("a2이 Tiger 타입인지 확인 : " + (a2 instanceof Tiger));
/* 상속 받은 타입도 가지고 있다. */
System.out.println("a1이 Animal 타입인지 확인 : " + (a1 instanceof Animal));
System.out.println("a2이 Animal 타입인지 확인 : " + (a2 instanceof Animal));
/* 모든 클래스는 Object의 후손이다. */
System.out.println("a1이 Object 타입인지 확인 : " + (a1 instanceof Object));
System.out.println("a2이 Object 타입인지 확인 : " + (a2 instanceof Object));
/* 해당 타입이 맞는 경우에만 클래스 형변환을 수행 */
if(a1 instanceof Rabbit) ((Rabbit)a1).jump();
if(a2 instanceof Tiger) ((Tiger)a2).bite();
/* up-casting : 상위 타입으로 형변환(묵시적, 자동 형변환)
* down-casting : 하위 타입으로 형변환(명시적, 강제 형변환)
* */
Animal animal1 = (Animal) new Rabbit();
Animal animal2 = new Rabbit();
Rabbit rabbit1 = (Rabbit) animal1;
// Rabbit rabbit2 = animal1;
}
}