다형성
다형성이란?
객체지향개념에서 다형성이란 ‘여러 가지 형태를 가질 수 있는 능력’을 말한다. 자바에서는 한 타입의 참조변수로 여러 타입의 객체를 참조할 수 있도록 함으로써 다형성을 프로그램적으로 구현하였다. 다시 말해, 조상클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조할 수 있도록 하였다.
참조변수의 타입은 참조할 수 있는 객체의 종류와 사용할 수 있는 멤버의 수를 결정한다.
참조변수가 사용할 수 있는 멤버의 개수는 인스턴스 멤버의 개수보다 같거나 적어야 한다. 따라서 조상타입의 참조변수로 자손 타입의 인스턴스를 참조할 수 있지만, 자손 타입의 참조변수로 조상 타입의 인스턴스를 참조할 수는 없다.
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 Tv {
boolean power;
int channel;
void power() {
power = !power;
}
void channelUp() {
++channel;
};
void channelDown() {
--channel;
};
}
class CaptionTv extends Tv {
String text;
void caption() {};
}
public class polymorphism {
public static void main(String[] args) {
Tv tv = new CaptionTv();
//tv.text = "5"; // 자식 클래스의 필드 접근X
//tv.caption(); // 자식 클래스의 메서드 접근X
tv.power();
tv.channel = 10;
}
}
참조변수의 형변환
서로 상속 관계에 있는 클래스 사이에서만 참조변수의 형변환이 가능하다.
Up-casting
- 자손타입 -> 조상타입
- 형변환 생략 가능
- 참조변수가 다룰 수 있는 멤버의 개수가 실제 인스턴스가 갖고 있는 멤버의 개수보다 적을 것이 분명하므로 문제가 되지 않기 때문
Down-casting
- 조상타입 -> 자손타입
- 명시적 형변환 필요
- 참조 변수가 다룰 수 있는 멤버의 개수를 늘리는 것이므로, 실제 인스턴스의 멤버 개수보다 참조변수가 사용할 수 있는 멤버의 개수가 더 많아지므로 문제가 발생할 가능성이 있다.
형변환 주의할 점
형변환은 참조 변수의 타입을 변환하는 것이지 인스턴스를 변환하는 것은 아니기 때문에 참조변수의 형변환은 인스턴스에 아무런 영향도 미치지 않는다. 단지 참조변수의 형변환을 통해서, 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 범위(개수)를 조절하는 것뿐이다.
참조변수가 참조하고 있는 인스턴스의 자손타입으로 형변환하는 것은 허용되지 않는다. 컴파일 시에는 참조변수간의 타입만 체크하기 때문에 실행 시 생성될 인스턴스 타입에 대해서는 전혀 알지 못한다. 그래서 컴파일 시에는 문제가 없지만, 실행 시 에러가 발생한다.
instanceof 연산자
instanceof를 이용한 연산결과로 true를 얻었다는 것은 참조변수가 검사한 타입으로 형변환이 가능하다는 것을 의미한다. 그러나 실제 인스턴스와 같은 타입의 참조변수로 형변환을 해야만 인스턴스의 모든 멤버들을 사용할 수 있다.
매개변수의 다형성
참조변수의 다형적인 특징은 메서드의 매개변수에도 적용된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
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;
}
매개변수의 다형성 적용 전: 타입에 따라 메서드를 중복적으로 정의해야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void buy (Tv t) {
money = money - t.price;
bonusPoint = bonusPoint + t.bonusBoint;
}
void buy (Computer c) {
money = money - c.price;
bonusPoint = bonusPoint + c.bonusPoint;
}
void buy (Audio a) {
money = money - a.price;
bonusPoint = bonusPoint + a.bonusPoint;
}
매개변수의 다형성 적용 후: 하나의 메서드로 처리할 수 있다.
1
2
3
4
void buy (Product p) {
money = money - p.price;
bonusPoint = bonusPoint + c.bonusPoint;
}
다형성이 적용된 전체 프로그램은 다음과 같다.
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
49
50
51
52
53
54
55
56
57
58
59
60
package com.monica.textbook.chap7;
class Product {
int price;
int bonusPoint;
Product(int price) {
this.price = price;
bonusPoint = (int) (price / 10.0);
}
}
class Tv extends Product {
Tv() {
super(100);
}
public String toString() {
return "Tv";
}
}
class Computer extends Product {
Computer() {
super(200);
}
public String toString() {
return "Computer";
}
}
class Buyer {
int money = 1000;
int bonusPoint = 0;
void buy(Product p) {
if (money < p.price) {
System.out.println("잔액이 부족하여 물건을 살 수 없습니다.");
return;
}
money -= p.price;
bonusPoint += p.bonusPoint;
System.out.println(p + "을 구입하셨습니다.");
}
}
public class PolyArgumentTest {
public static void main(String[] args) {
Buyer b = new Buyer();
b.buy(new Tv());
b.buy(new Computer());
System.out.println("현재 남은 돈은 " + b.money + "만원입니다.");
System.out.println("현재 보너스 점수는 " + b.bonusPoint + "점입니다.");
}
}
print(Object o)
1
2
3
4
5
6
7
public void print(Object obj) {
write(String.valueOf(obj)); // valueOf가 반환하는 문자열 출력
}
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
여러 종류의 객체를 배열로 다루기
조상 타입의 참조변수 배열을 사용하면, 공통의 조상을 가진 서로 다른 종류의 객체를 배열로 묶어서 다룰 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Buyer {
int money = 1000;
int bonusPoint = 0;
Product[] item = new Product[10];
int i = 0;
void buy(Product p) {
if (money < p.price) {
System.out.println("잔액이 부족하여 물건을 살 수 없습니다.");
return;
}
money -= p.price;
bonusPoint += p.bonusPoint;
item[i++] = p;
System.out.println(p + "을 구입하셨습니다.");
}
}
Product 배열을 사용하였지만, 배열의 크기가 유동적이지 않다. 이런 경우 Vector 클래스를 사용하자. Vector 클래스는 내부적으로 Object 타입의 배열을 가지고 있고, 객체를 추가하거나 제거할 수 있도록 작성되어 있다. 또한 배열의 크기를 알아서 관리해준다(동적으로 크기가 관리되는 객체배열).
1
2
3
4
public class Vector extends AbstractList implements List, Cloneable, java.io.Serializable {
protected Object elementData[];
//..
}