Posts :book: 자바의 정석 #7.5: 다형성
Post
Cancel

:book: 자바의 정석 #7.5: 다형성

다형성

다형성이란?

객체지향개념에서 다형성이란 ‘여러 가지 형태를 가질 수 있는 능력’을 말한다. 자바에서는 한 타입의 참조변수로 여러 타입의 객체를 참조할 수 있도록 함으로써 다형성을 프로그램적으로 구현하였다. 다시 말해, 조상클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조할 수 있도록 하였다.

  • 참조변수의 타입은 참조할 수 있는 객체의 종류와 사용할 수 있는 멤버의 수를 결정한다.

  • 참조변수가 사용할 수 있는 멤버의 개수는 인스턴스 멤버의 개수보다 같거나 적어야 한다. 따라서 조상타입의 참조변수로 자손 타입의 인스턴스를 참조할 수 있지만, 자손 타입의 참조변수로 조상 타입의 인스턴스를 참조할 수는 없다.

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[];
    //..
}
This post is licensed under CC BY 4.0 by the author.

학원 #33일차: 인터페이스, 추상 클래스, 싱글톤

:book: 자바의 정석 #7.6: 추상클래스

Loading comments from Disqus ...