일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 그래프
- 다익스트라
- Two Points
- Trie
- binary search
- 이진탐색
- Stored Procedure
- MYSQL
- two pointer
- 스토어드 프로시저
- DP
- Dijkstra
- SQL
- Hash
- String
- union find
- Brute Force
- Today
- Total
codingfarm
다형성(polymorphism) 본문
1. 다형성이란?
- 정의 : 여러가지 형태를 가질 수 있는 능력
- OOP의 4대 특징 중 하나
- 추상화
- 캡슐화
- 상속성
- 다형성
- JAVA에서는 조상클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조할 수 있도록 하여 다형성을 구현한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class Tv{
boolean power;
int channel;
void power() { power = !power; }
void channelUp() { ++channel; }
void channelDown() { --channel; }
}
class CaptionTv extends Tv{
String text; // 캡션을 보여주기 위한 문자열
void caption() {/*...*/}
}
|
cs |
Tv 클래스와 CaptionTv 클래스가 위와 같이 정의되어 있을때, 두 클래스간의 관계를 그림으로 그리면 아래와 같다.
다음과 같이 조상 클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조하도록 하는 것도 가능하다.
1
|
Tv t = new CaptionTv();
|
cs |
인스턴스를 같은 타입의 참조변수로 참조하는 것과 조상 타입의 참조변수로 참조하는것은 어떤 차이가 있을까?
모든 클래스의 최고조상인 Object클래스로부터 상속받은 부분은 생략했다.
Tv 타입의 참조변수로는 CaptionTv 인스턴스 중에서 Tv클래스의 멤버들만 사용할 수 있다. 따라서, 생성된 CaptionTv 인스턴스의 멤버 중에서 Tv 클래스에 정의되지 않은 멤버, text와 catption()은 참조변수 t로 사용이 불가능하다.
자식 클래스의 참조변수는 부모 클래스의 인스턴스를 참조 할 수 없다.
1
|
CaptionTv c = new Tv(); // 컴파일 에러
|
cs |
참조변수가 사용할 수 있는 멤버의 개수는 인스턴스의 멤버 개수보다 같거나 적어야 한다.
조상타입의 참조변수로 자손타입의 인스턴스를 참조할 수 있다.
2. 참조변수의 형변환
- 다운캐스팅(down-casting) : 조상타입의 참조변수를 자손타입의 참조변수로 변환하는 것
- 업 캐스팅(up-casting) : 자손 타입의 참조변수를 조상 타입의 참조변수로 변환하는것
- 업 캐스팅을 위해선 형변환이 필요하다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
class Car {
String color;
int door;
void drive() {
System.out.println("drive, Brrrr~");
}
void stop() {
System.out.println("stop!!!");
}
}
class FireEngine extends Car{ // 소방차
void water() {
System.out.println("water!!!");
}
}
class Ambulance extends Car{ // 앰뷸련스
void siren() {
System.out.println("siren~~~");
}
}
|
cs |
위 클래스간의 관계를 그림으로 나타내면 아래와 같다.
상속관계가 아닌 클래스의 참조변수와 인스턴스간에는 형변환을 한다 할지라도 참조할 수 없다.
1
2
3
4
5
|
FireEngine f;
Ambulance a;
a = (Ambulance) f; // 컴파일 에러!!
f = (FireEngine) a; // 컴파일 에러!!
|
cs |
부모 자식 관계에 있는 클래스의 참조변수 간의 형변환은 아래 처럼 이루어진다.
1
2
3
4
5
6
|
Cat car = null;
FireEngine fe = new FireEngine();
FireEngine fe2 = null;
car = fe;
fe2 = (FireEngine)car;
|
cs |
아래의 코드는 에러를 발생시킨다.
1
2
|
Cat car = new Car();
FireEngine fe = (FireEngine) car; // 에러
|
cs |
참조변수 car가 참조하고 있는 인스턴스가 Car 타입의 인스턴스 이다. 조상타입의 인스턴스를 자손타입의 참조변수로 참조하는것은 허용되지 않는다.
$\bullet$ 캐스트 연산자를 사용하면 서로 상속 관계에 있는 클래스 타입의 참조변수간의 형변환은 양방향으로 자유롭게 수행될 수 있다.
$\bullet$ 참조변수가 참조하고 있는 인스턴스의 자손타입으로 형변환을 하는 것은 허용되지 않는다.
3. instanceof 연산자
- 참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 사용한다.
- instanceof를 기준으로 각각의 피연산자가 위치한다.
- 왼쪽 : 참조 변수
- 오른쪽 : 타입(클래스명)
- 결과 : boolean(true or false)
- true : 참조변수가 검사한 타입으로 형변환이 가능하다.
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
|
package project_1;
class Car {
String color;
int door;
void drive() {
System.out.println("drive, Brrrr~");
}
void stop() {
System.out.println("stop!!!");
}
}
class FireEngine extends Car{ // 소방차
void water() {
System.out.println("water!!!");
}
}
class Ambulance extends Car{ // 앰뷸련스
void siren() {
System.out.println("siren~~~");
}
}
public class Hello {
static 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();
// ...
}
// ...
}
public static void main(String[] args) {
Car c = new FireEngine();
doWork(c);
}
}
|
cs |
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
|
package project_1;
class Car {
String color;
int door;
void drive() {
System.out.println("drive, Brrrr~");
}
void stop() {
System.out.println("stop!!!");
}
}
class FireEngine extends Car{ // 소방차
void water() {
System.out.println("water!!!");
}
}
public class Hello {
public static void main(String[] args) {
FireEngine fe = new FireEngine();
if(fe instanceof FireEngine) {
System.out.println("This is a FireEngine instance.");
}
if(fe instanceof Car) {
System.out.println("This is a Car instance.");
}
if(fe instanceof Object) {
System.out.println("This is a Object instance.");
}
}
}
|
cs |
어떤 타입에 대한 instanceof 연산의 결과가 true라는 것은 검사한 타입으로 형변환이 가능하다는 것을 뜻한다.
4. 참조변수와 인스턴스의 연결
- 조상 클래스와 자식 클래스에 같은 이름의 필드와 메서드가 중복으로 정의되었을때, 부모와 자식 클래스중 어느 인스턴스를 참조하는지에 따라 실제 접근하는 필드와 메서드는 달라진다.
- 메서드 : 항상 실제 인스턴스의 메서드(오버라이딩된 메서드) 가 호출
- 필드 : 참조변수의 타입에 따라 달라진다.
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
|
package project_1;
class Parent {
int x = 100;
void method() {
System.out.println("Parent Method");
}
}
class Child extends Parent {
int x = 200;
void method() {
System.out.println("Child Method");
}
}
public class Hello {
public static void main(String[] args) {
Parent p = new Child();
Child c = new Child();
System.out.println("p.x = " + p.x);
p.method();
System.out.println("c.x = " + c.x);
c.method();
}
}
|
cs |
method의 경우 항상 인스턴스의 메서드(Child의 메서드)를 호출하지만 필드의 경우 각 참조변수의 타입을 참조하는것을 볼 수 있다.
부모와 자식 클래스에 이름이 같은 멤버변수가 존재한다면 참조변수의 타입에 따라 접근하는 멤버변수가 달라진다.
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
|
package project_1;
class Parent {
int x = 100;
void method() {
System.out.println("Parent Method");
}
}
class Child extends Parent {
int x = 200;
void method() {
System.out.println("x = " + x);
System.out.println("super.x = " + super.x);
System.out.println("this.x = " + this.x);
}
}
public class Hello {
public static void main(String[] args) {
Parent p = new Child();
Child c = new Child();
System.out.println("p.x = " + p.x);
p.method();
System.out.println();
System.out.println("c.x = " + c.x);
c.method();
}
}
|
cs |
5. 매개변수의 다형성
- 참조변수의 다형적인 특징은 메서드의 매개변수에도 적용가능하다.
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;
}
|
cs |
Buyer클래스에 물건을 구입하는 기능의 메서드를 추가해보자. Product 하위 클래스 제품에 대해 각각 다른 메서드를 정의하면 아래와 같을것이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class Buyer {
int money = 1000;
int bonusPoint = 0;
void buy(Tv t) {
money = money - t.price;
bonusPoint = bonusPoint + t.bonusPoint;
}
void buy(Computer c) {
money = money - c.price;
bonusPoint = bonusPoint + c.bonusPoint;
}
void buy(Audio a) {
money = money - a.price;
bonusPoint = bonusPoint + a.bonusPoint;
}
}
|
cs |
이런식으로 메서드를 작성할 경우 2가지 단점이 존재한다.
- 제품의 종류가 늘어날때마다 함수를 새로 작성해야한다.
- buy 메서드의 내부 로직에 변화가 생기면 모든 메서드를 다 수정해야한다.
그러나 메서드의 매개변수에 다형성을 적용하면 아래처럼 하나의 메서드로 간단히 처리될 수 있다.
1
2
3
4
5
6
7
8
9
|
class Buyer {
int money = 1000;
int bonusPoint = 0;
void buy(Product p) {
money = money - p.price;
bonusPoint = bonusPoint + p.bonusPoint;
}
}
|
cs |
아래 예제코드를 통해 매개변수의 다형성에 대해 알 수 있다.
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
package project_1;
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 Audio extends Product {
Audio(){
super(300);
}
public String toString() {
return "Audio";
}
}
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 Hello {
public static void main(String[] args) {
Buyer b = new Buyer();
Product tv = new Tv();
Product a = new Audio();
Product com = new Computer();
/*
Tv tv = new Tv();
Audio a = new Audio();
Computer com = new Computer();
*/
b.buy(tv);
b.buy(com);
b.buy(a);
System.out.println("현재 남은 돈은 " + b.money + "만원입니다.");
System.out.println("현재 남은 보너스점수는 " + b.bonusPoint + "점입니다.");
}
}
|
cs |
6. 여러종류의 객체를 하나의 배열로 다루기
- 다형성의 특성을 이용하여 여러 종류의 객체를 하나의 참조변수 배열로 다룰 수 있다.
1
2
3
4
|
Product p[] = new Product[3];
p[0] = new Tv();
p[1] = new Computer();
p[2] = new Audio();
|
cs |
'Programming Language > JAVA' 카테고리의 다른 글
인터페이스(interface) (0) | 2021.03.11 |
---|---|
추상클래스(abstract class) (0) | 2021.03.11 |
제어자(modifier) (0) | 2021.03.07 |
super (0) | 2021.01.07 |
오버라이딩(overriding) (0) | 2021.01.07 |