7.5 다형성
1. 다형성(Polymorphism)이란? (1)
- 여러 가지 형태를 가질 수 있는 능력을 의미한다.
- 하나의 참조변수로 여러 타입의 객체를 참조할 수 있는 것을 의미한다. 즉, 조상타입의 참조변수로 자손타입의 객체를 다룰 수 있는 것이 다형성이다.
package javaStandard;
class Tv{
boolean power; //전원상태(on/off)
int channel; //채널
void power() {
power = !power;
}
void channelUp() {
++channel;
}
void channelUDown() {
--channel;
}
}
class CaptionTv extends Tv{
String text; //캡션내용
void caption() {
// 내용생략
}
}
CaptionTv c = new CaptionTv(); //참조 변수와 인스턴스의 타입이 일치
Tv t = new CaptionTv(); // 조상 타입 참조변수로 자손 타입 인스턴스 참조
//타입이 일치하는 경우는 원래 OK
Tv t = new Tv();
CaptionTv c = new CaptionTv();
↓
Tv t = new CaptionTv();
//타입이 불일치하는 경우에도 OK ---> 다형성!!!!
//조상타입이 참조변수도 자손타입이 실제 객체
타입이 불일치하는 경우의 장점(2가지)가 있다.
1. 다형성(Polymorphism)이란? (2)
기능의 일부만 사용할 수 있는 것이 어떻게 다형성의 장점이 될 수 있을까?
일단은 참조변수 타입과 인스턴트 타입이 다를 때 어떤지를 확실히 알아두기로 하자
1. 다형성(Polymorphism)이란? (3)
- 조상타입의 참조변수로 자손타입의 인스턴스를 참조할 수 있지만, 반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수는 없다.
Tv t = new CaptionTv(); //허용 / 조상타입의 참조변수로 자손 타입의 객체를 가리키는 경우
CaptionTv c = new Tv(); // 에러 --> 허용 안 됨 / 자손타입의 참조변수로 조상 타입의 객체를 가리키는 경우
2. 참조변수의 형변환
- 서로 상속관계에 있는 타입간의 형변환만 가능하다.
- 자손 타입에서 조상타입으로 형변환하는 경우, 형변환 생략이 가능하다.
자손타입 -> 조상타입 (Up - Casting) : 형변환 생략가능
조상타입 -> 자손타입 (Down - Casting) : 형변환 생략불가
package javaStandard;
class Car{
String color;
int door;
void drive() {
System.out.println("drive, Brrrrr~");
}
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~~");
}
}
}
결론 : 사용할 수 있는 멤버의 갯수를 조절하는 것이다. / 기본형 형변환처럼 값이 달라지는 게 아니다. 멤버의 갯수만 바뀐다.
- 조상 자손 관계는 형변환이 가능하지만 형제 관계는 형변환이 불가하다.
package javajungsuk;
public class Ex_7_7 {
public static void main(String[] args) {
Car car = null;
FireEngine fe = new FireEngine();
FireEngine fe2 = null;
fe.water();
car = fe; //car = (Car)fe; 형변환 생략
//car.water ... 컴파일 에러 Car타입의 참조변수로는 water()를 호출할 수 없다
fe2 = (FireEngine)car; // 조상타입 -> 자손타입 형변환 생략불가
fe2.water()
}
}
package javajungsuk;
public class Ex_7_7 {
public static void main(String[] args) {
Car car = null;
FireEngine fe = new FireEngine();
FireEngine fe2 = null;
fe.water();
car = fe;
//car.water ... 컴파일 에러 Car타입의 참조변수로는 water()를 호출할 수 없다
fe2 = (FireEngine)car; // 조상타입 -> 자손타입 형변환 생략불가
fe2.water()
}
}
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!!!");
}
}
}
<Null을 가리키고 있을 때>
package javajungsuk;
public class Ex7_7_2 {
public static void main(String[] args) {
Car car = null;
FireEngine fe = null; //실제 인스턴스가 무엇인지가 중요하다.
FireEngine fe2 = (FireEngine)car; // 조상 -> 자손으로 형변환
Car car2 = (Car)fe2; // 자손 - > 조상으로 형변환
car2.drive(); // NullPointerException 발생 참조변수가
}
}
<참조변수가 가르키고 있는 인스턴스(객체)가 중요한 예시>
package javajungsuk;
public class Ex7_7_2 {
public static void main(String[] args) {
Car c = new Car();
FireEngine fe = (FireEngine)c; //형변환 에러 java.lang.ClassCastException
fe.water();//컴파일은 가능 ClassCastException 발생!!
//실제 가르키고 있는 객체는 Car 이기 때문에 4개의 기능만 사용가능해서
//water()는 사용 불가
}
}
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!!!");
}
}
3. instanceof 연산자
- 참조변수의 형변환 가능여부 확인에 사용한다. 가능하다면 True를 반환한다.
- 형변환 해도 되는지를 확인한 뒤
- 형변환을 한다
- 형변환 전에 반드시 instanceof()를 확인하고 진행해야 한다.
void dowork(Car c){
if(c instanceof FireEngine) {
FireEngine fe = (FireEngine)c;
fe.water();
}
else if (c instanceof Ambulance){
Ambulance a = (Ambulance)c;
}
}
<상속계층도를 이용해서 알아보는 instanceof 연산자>
void dowork(Car c){
if(c instanceof FireEngine) {
FireEngine fe = (FireEngine)c;
fe.water();
}
else if (c instanceof Ambulance){
Ambulance a = (Ambulance)c;
}
}
FireEngine fe = new FireEngine();
System.out.println(fe.instanceof Object); // true
System.out.println(fe.instanceof Car); //true
System.out.println(fe.instanceof FireEngine); //자기자신 true
형변환을 했을 때 객체가 달라지는 것이 아니고 참조변수의 값도 그대로고 타입만 달라지는 것이다.
4. 참조변수와 인스턴스변수의 연결
- 멤버변수가 중복정의된 경우, 참조변수의 타입에 따라 연결되는 멤버변수가 달라진다. (참조변수타입에 영향받음)
- 메서드가 중복정의된 경우, 참조변수의 타입에 관계 없이 항상 실제 인스턴스의 타입에 정의된 메서드가 호출된다. (참조변수 타입에 영향을 받지 않는다.)
5. 매개변수의 다형성
- 참조형 매개변수는 메서드 호출시, 자신과 같은 타입 또는 자손타입의 인스턴스를 넘겨줄 수 있다.
package javajungsuk;
//부모
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 -= c.price;
bonusPoint += c.bonusPoint;
}
void buy(Computer c) {
money -= c.price;
bonusPoint += c.bonusPoint;
}
void buy(Audio c) {
money -= c.price;
bonusPoint += c.bonusPoint;
}
↓
package javajungsuk;
//부모
class Product{
int price;//제품가격
int bonusPoint;//보너스점수
Product(int price){
this.price = price;
bonusPoint = (int)(price/10.0); //보너스 점수는 제품 가격의 10%
}
}
//자손
class Tv extends Product {
Tv(){
//조상 클래스의 생성자 Product(int price)를 호출
super(100); //Tv 가격 100만원으로 설정
}
public String toString() {
return "Tv";
}
}
class Computer extends Product {
Computer() {
super(200);
}
public String toString() {
return "Computer";
}
}
class Audio extends Product {
Audio(){
super(50);
}
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 + "을/를 구입하셨습니다.");
}
}
class java7_5_5{
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+"점입니다.");
}
}
6. 여러 종류의 객체를 하나의 배열로 다루기
- 조상 타입의 배열에 자손들의 객체를 담을 수 있다.
- 하나의 배열에 여러종류 객체 저장이 가능한 것이 다형성의 장점 중 하나다.
product p1 = new Tv();
product p2 = new Computer();
product p3 = new Audio();
↓
Product p[] = new Product[3];
p[0] = new Tv();
p[1] = new Computer();
p[2] = new Audio();
다형성때문에 product 객체뿐만 아니고 자식들이 다 들어갈 수 있다.
package javajungsuk;
//부모
class Product2{
int price;//제품가격
int bonusPoint;//보너스점수
Product2(int price){
this.price = price;
bonusPoint = (int)(price/10.0); //보너스 점수는 제품 가격의 10%
}
}
//자손
class Tv2 extends Product2 {
Tv2(){
//조상 클래스의 생성자 Product2(int price)를 호출
super(100); //Tv 가격 100만원으로 설정
}
public String toString() {
return "Tv";
}
}
class Computer2 extends Product2 {
Computer2() {
super(200);
}
public String toString() {
return "Computer";
}
}
class Audio2 extends Product2 {
Audio2(){
super(50);
}
public String toString() {
return "Audio";
}
}
class Buyer2 { //물건을 사는 사람
int money = 1000; //소유금액
int bonusPoint = 0; //보너스점수
Product2[] cart = new Product2[10]; //구입제품 저장하기 위한 배열
int i = 0; // Product 배열에 사용될 카운터
void buy(Product2 p) {
if (money < p.price) {
System.out.println("잔액이 부족하여 물건을 살 수 없습니다.");
return;
}
money -= p.price;
bonusPoint += p.bonusPoint;
cart[i++] = p;
System.out.println(p.toString() + "을/를 구입하셨습니다.");
}
void summary() {
int sum = 0;
String itemlist = "";
for(int i = 0; i < cart.length; i++) {
if(cart[i]==null) break;
sum += cart[i].price;
itemlist += cart[i] +", ";
}
System.out.println("구입하신 물품의 총금액은" + sum +"만원입니다.");
System.out.println("구입하신 제품은" +itemlist +"입니다.");
}
}
class java7_6_1{
public static void main(String args[]) {
Buyer2 b = new Buyer2();
b.buy(new Tv2());
b.buy(new Computer2());
b.buy(new Audio2());
b.summary();
}
}
Tv을/를 구입하셨습니다.
Computer을/를 구입하셨습니다.
Audio을/를 구입하셨습니다.
구입하신 물품의 총금액은350만원입니다.
구입하신 제품은Tv, Computer, Audio, 입니다.
※ 다형성이 자바의 꽃이기 때문에 이를 확실히 이해하지 못하면 넘어가지 말 것 ~
정리
Q1. 참조변수 타입은 인스턴스의 타입과 반드시 일치해야 하나요?
A1. 아니다 일치하는 것이 보통이지만 일치하지 않을 수도 있다.
Q2. 참조변수가 조상타입일 때와 자손타입일 때의 차이?
A2. 참조변수로 사용할 수 있는 멤버의 갯수가 달라진다.
Q3. 자손 타입의 참조변수로 조상 타입의 객체를 가리킬 수 있나요?
A3. 허용되지 않는다.
'Java > 객체지향' 카테고리의 다른 글
[객체지향][자바의 정석] - 7.7 인터페이스(Interface) (0) | 2021.12.03 |
---|---|
[객체지향][자바의 정석] - 7.6 추상클래스(Abstract class) (0) | 2021.11.28 |
[객체지향][자바의 정석] - 7.4 제어자 (0) | 2021.11.11 |
[객체지향][자바의 정석] - 7.2 오버라이딩(Overriding) (0) | 2021.11.08 |
[객체지향][자바의 정석] - 7.1 상속(inheritance) (0) | 2021.11.04 |