다형성이란?
'서로 다른 형태'를 지칭하는 말로 객체지향 프로그래밍의 3대 특징 중 하나이며, 여러 객체들을 한번에 처리할 수 있는 기술이다.
상속을 이용한 기술로, 부모 타입으로부터 파생된 여러 가지 타입을 하나의 타입인 것처럼 처리할 수 있다.
상위 타입으로 하위 타입의 객체를 사용할 수 있다.
클래스 형변환(up casting)
상속 관계에 있는 부모 타입의 참조형 변수가 모든 자식 타입의 객체의 주소를 받을 수 있다.
자식 객체의 주소를 전달받은 부모타입의 참조변수는 원래 부모타입의 멤버만 참조 가능하다.
Sonata 클래스가 Car 클래스의 후손인 경우
Car c = new Sonata();
Sonata 클래스형 -> Car 클래스 형으로 바뀜
클래스 형변환 (down casting)
자식 객체의 주소를 받은 부모 참조형 변수를 가지고 자식의 멤버를 참조해야 할 경우, 후손 클래스 타입으로 참조형 변수를 형 변환해야 한다.
이 변환을 down casting이라고 하며, 자동으로 처리되지 않기 때문에 반드시 후손 타입을 명시해서 형 변환 해야 한다.
Sonata클래스가 Car 클래스의 후손일 경우
Car c = new Sonata();
((Sonata)c).moveSonata();
어느 후손클래스의 멤버를 사용하는지 명확히 알려줘야 한다.
클래스간의 형 변환은 반드시 상속 관계에 있는 클래스끼리만 가능하다
객체 배열과 다형성
다형성을 이용하여 상속 관계에 있는 여러 개의 자식클래스를 부모 클래스의 배열에 저장 가능
Car[] carr = new Car[5]; //부모클래스
carr[0] = new Sonata(); //자식클래스
carr[1] = new Avante(); //자식클래스
carr[2] = new Spark();
carr[3] = new Morning();
carr[4] = new Grandure();
매개변수와 다형성
메소드 호출시 다형성을 이용하여 부모타입의 매개변수를 사용하면, 자식타입의 객체를 받을 수 있다.
angryCar(new Sonata()); //자식클래스
angryCar(new Avante());
angryCar(new Grandure();
public void angryCar(Carc){ //Car 부모클래스
}
instanceof 연산자
현재 참조형 변수가 어떤 클래스 형의 객체 주소를 참조하고 있는지 확인할 때 사용한다.
클래스타입이 맞으면 true, 아니면 false값을 반환
if(레퍼런스 instanceof 클래스타입){
//참일
}
정적 바인딩
Sonata s = new Sonata();
s.move();
동적 바인딩
정적으로 작성 + new Sonata();되어 있는 코드가 실행될 때 sonata를 호출해야 한다는 것 동적 바인딩이라고 한다.
Car c = new Sonata();
c.move();
게임 과제
코딩하기 전에
클래스 다이어그램으로 먼저 그려보고
어떤걸 호출할건지 그려보고
어떻게 만들건지 구상을 한 다음에 코딩하기
ProductManager
package com.kh.poly.part02_abstractClassAndInterface.product.controller;
import com.kh.poly.part02_abstractClassAndInterface.product.model.vo.Book;
import com.kh.poly.part02_abstractClassAndInterface.product.model.vo.Galaxy;
import com.kh.poly.part02_abstractClassAndInterface.product.model.vo.Product;
import com.kh.poly.part02_abstractClassAndInterface.product.model.vo.SmartPhone;
public class ProductManager {
public void testPolymorphism() {
//다형성(Polymorphism)
//상속관계에 있는 클래스인 경우
//부모클래스 타입의 레퍼런스 변수가 자식 클래스 타입의 객체의 주소를 저장할 수 있다.
//추상클래스를 객체로 생성하지 못하고 상속받은 하위 타입의 클래스로 객체를 생성해야 한다.
//추상클래스를 타입으로서는 사용 가능하지만 객체를 생성하지 못한다.
//Product p = new Product();
Product p1 = new SmartPhone();
Product p2 = new Galaxy();
Product p3 = new Book();
System.out.println(p1);
System.out.println(p2);
System.out.println(p3);
//Object는 모든 클래스의 조상이다.
Object obj1 = p1;
Object obj2 = p2;
Object obj3 = p3;
//후손 클래스 타입으로 부모 객체의 주소를 저장할 수 없다.
//강제로 형변환을 해도 SmartPhone은 Galaxy 타입이 아니기 때문에 형변환 할 수 없다.
//ClassCastExecption 발생!
//Galaxy g = (Galaxy) new SmartPhone();
//자식 클래스의 메소드를 호출하기 위해 부모 레퍼런스를 자식 레퍼런스로 형변환 할 수 있다.
//Runtime시에 레퍼런스 변수에 들어있는 객체가 형변환하려는 타입이 아닌 경우
//ClassCastException이 발생한다.
SmartPhone s1 = new Galaxy();
s1.printProduct();
s1.printSmartPhone();
//자식클래스의 메소드를 호출하기 위해서 부모 레퍼런스를 자식 레퍼런스로 형변환 할 수 있다.
((Galaxy)s1).printGalaxy();
//매개변수에도 다형성이 적용될 수 있다.
print(new SmartPhone());
}
public void print(Object obj) {
System.out.println("======================");
// if(obj instanceof Galaxy) {
// ((Galaxy) obj).printGalaxy();
// }
//
// if(obj instanceof Book) {
// ((Book) obj).printBook();
// }
//
// if(obj instanceof SmartPhone) {
// ((SmartPhone) obj).printSmartPhone();
// }
//
// if(obj instanceof Product) {
// ((Product) obj).printProduct();
// }
//후손 타입에 대한 비교를 위쪽에 작성해야 한다.
if(obj instanceof Galaxy) {
((Galaxy) obj).printGalaxy();
} else if(obj instanceof Book) {
((Book) obj).printBook();
} else if(obj instanceof SmartPhone) {
((SmartPhone) obj).printSmartPhone();
} else if(obj instanceof Product) {
((Product) obj).printProduct();
}
}
public void objectArrayTest() {
//객체 배열에도 다형성을 적용할 수 있다.
Object[] arr = new Object[10];
arr[0] = new String("문자열 객체");
arr[1] = new java.util.Date();
//arr[2] = new Product(); //추상클래스는 객체 생성 불가!
arr[3] = new Book();
arr[4] = new Galaxy();
arr[5] = new SmartPhone();
for(int i = 0; i < arr.length; i++) {
//System.out.println(arr[i].toString());
if(arr[i] instanceof Galaxy) {
((Galaxy) arr[i]).printGalaxy();
} else if(arr[i] instanceof Book) {
((Book) arr[i]).printBook();
} else if(arr[i] instanceof SmartPhone) {
((SmartPhone) arr[i]).printSmartPhone();
} else if(arr[i] instanceof Product) {
((Product) arr[i]).printProduct();
}
}
}
}
Book
package com.kh.poly.part02_abstractClassAndInterface.product.model.vo;
import java.io.Serializable;
//인터페이스 상속의 경우 implements 키워드 사용한다.
//클래스에서 다른 클래스를 extends 키워드를 이용하여 상속받는 경우 다중 상속이 불가능하지만
//implements를 이용하여 다른 interface를 다중 상속하는 것을 지원하여 단일 상속의 한계를 극복할 수 있다.
public class Book extends Product implements IProduct, Serializable {
public Book() {}
public void printBook() {
System.out.println("Book 클래스의 printBook() 실행...");
}
@Override
public String toString() {
return "북 클래스의 toString() 메소드 실행...";
}
@Override
public int compare(Object o1, Object o2) {
// TODO Auto-generated method stub
return 0;
}
@Override
public void abstIMethod() {
// TODO Auto-generated method stub
}
@Override
public void abstMethod() {
// TODO Auto-generated method stub
}
}
Galaxy
package com.kh.poly.part02_abstractClassAndInterface.product.model.vo;
public class Galaxy extends SmartPhone {
//기본생성자 작성
public Galaxy() {}
public void printGalaxy() {
System.out.println("Galaxy 클래스의 printGalaxy() 메소드 실행...");
}
@Override
public String toString() {
return "Galaxy 클래스의 toString() 메소드 실행...";
}
}
Iproduct
package com.kh.poly.part02_abstractClassAndInterface.product.model.vo;
import java.io.Serializable;
import java.util.Comparator;
import java.util.Iterator;
//인터페이스간의 상속은 extends를 사용하여 다중 상속을 지원한다.
//인터페이스간 상속은 implements 키워드 사용이 불가능하다.
//추상 메소드, 상수 필드만 멤버로 가질 수 있는 추상클래스의 변형체를 인터페이스라고 한다.
public interface IProduct extends Serializable, Comparator {
//상수 필드만 멤버로 가질 수 있기 때문에
//public static final의 의미를 묵시적으로 가진다.
public static final String PRODUCT_NAME = "상품명";
//추상 메소드만 멤버로 가질 수 있기 때문에
//public abstract의 의미를 묵시적으로 가진다.
public abstract void abstIMethod();
}
Product
package com.kh.poly.part02_abstractClassAndInterface.product.model.vo;
//추상메소드를 멤버로 가지는 경우에는 class앞에 반드시 abstract 키워드를 표시해야 한다.
//추상클래스는 미완성된 클래스를 의미하며,
//미완성된 메소드는 반드시 상속 후 오버라이딩 하여 완성을 해야 한다.
//추상클래스는 생성자는 작성할 수 있지만 객체를 생성하지는 못한다.
public abstract class Product {
public Product() {}
//추상메소드(미완성 메소드) : 메소드 헤드만 있고 바디가 없는 메소드
public abstract void abstMethod();
public void printProduct() {
System.out.println("Product 클래스의 메소드 실행...");
}
@Override
public String toString() {
return "Product의 toString() 메소드 실행...";
}
}
SmartPhone
package com.kh.poly.part02_abstractClassAndInterface.product.model.vo;
public class SmartPhone extends Product {
public SmartPhone() {}
public void printSmartPhone() {
System.out.println("SmartPhone 클래스의 printSmartPhone()메소드 실행...");
}
//상속받은 부모의 추상메소는 반드시 후손 클래스에서 오버리이딩 해서 완성을 해야 한다.
@Override
public void abstMethod() {}
@Override
public String toString() {
return "smartPhone 클래스의 toString() 메소드 실행...";
}
}
Run
package com.kh.poly.part02_abstractClassAndInterface.product.run;
import com.kh.poly.part02_abstractClassAndInterface.product.controller.ProductManager;;
public class Run {
public static void main(String[] args) {
ProductManager pm = new ProductManager();
pm.testPolymorphism();
}
}
PlayerManager
package com.kh.poly.part01_polymorphismTest.animal.controller.model.vo;
import com.kh.poly.part01_polymorphismTest.animal.model.vo.Animal;
import com.kh.poly.part01_polymorphismTest.animal.model.vo.Chicken;
import com.kh.poly.part01_polymorphismTest.animal.model.vo.Player;
import com.kh.poly.part01_polymorphismTest.animal.model.vo.Tiger;
public class PlayerManager {
public void startPlay(String name) {
Player p = new Player(name);
System.out.println(p.getName() + "님께서 등장하셨습니다.");
//치킨 등장
// Chicken c1 = new Chicken(100, 100, "초보닭");
// c1.cry();
//닭은 동물이기도 하기 때문에 동물 타입의 변수에 닭의 주소를 저장할 수 있다.
Animal c1 = new Animal(100, 100, "초보닭");
c1.cry();
//닭을 공격
if(c1 instanceof Chicken) {
((Chicken)c1).lossHp(); //다운캐스팅
}
for(int i = c1.getHp(); i > 0; i -= 10) {
if(c1 instanceof Chicken) {
((Chicken) c1).lossHp();
}
}
//경험치 증가
p.setExp(p.getExp() + c1.getExp());
//플레이어 정보 확인
System.out.println(p);
Animal t1 = new Tiger(1000, 1000, "시베리안호랑이");
for(int i = t1.getHp(); i > 0; i -=50) {
if(t1 instanceof Tiger) {
((Tiger) t1).lossHp();;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
p.setExp(p.getExp() + c1.getExp());
//플레이어 정보 확인
System.out.println(p);
}
}
Player
package com.kh.poly.part01_polymorphismTest.animal.model.vo;
public class Player {
private String name;
private int exp = 0;
public Player(String name) {
this.name = name;
}
public Player(int exp) {
this.exp = exp;
}
public String getName() {
return name;
}
public int getExp() {
return exp;
}
public void setName(String name) {
this.name = name;
}
public void setExp(int exp) {
this.exp = exp;
}
@Override
public String toString() {
return name + "님의 현재 경험치는 " + exp + "입니다.";
}
}
Animal
package com.kh.poly.part01_polymorphismTest.animal.model.vo;
public class Animal {
private int hp;
private int exp;
private String kinds;
public Animal() {}
public Animal(int hp, int exp, String kinds) {
super();
this.hp = hp;
this.exp = exp;
this.kinds = kinds;
}
public int getHp() {
return hp;
}
public int getExp() {
return exp;
}
public String getKinds() {
return kinds;
}
public void setHp(int hp) {
this.hp = hp;
}
public void setExp(int exp) {
this.exp = exp;
}
public void setKinds(String kinds) {
this.kinds = kinds;
}
public void cry() {
System.out.println("울음소리를 냅니다.");
}
}
Chicken
package com.kh.poly.part01_polymorphismTest.animal.model.vo;
public class Chicken extends Animal {
public Chicken(int hp, int exp, String kinds) {
super(hp, exp, kinds);
}
@Override
public void cry() {
System.out.println("닭이 꼬끼오~ 하고 울음소리를 냅니다.");
}
public void peck() {
System.out.println("닭이 쪼았습니다.");
}
public void lossHp() {
System.out.println("닭이 비명을 지르며 hp가 10만큼 감소합니다.");
super.setHp(super.getHp() -10);
if(super.getHp() <= 0) {
System.out.println("닭이 쓰러집니다. 경험치가 " + super.getExp() + "만큼 상승합니다.");
}
}
}
Tiger
'Programming > Java' 카테고리의 다른 글
[Java] Exception(예외처리) (0) | 2022.03.07 |
---|---|
[Java] 기본 API (0) | 2022.03.07 |
[Java] 상속(Ingerit) (0) | 2022.03.07 |
[Java] 배열 (0) | 2022.03.07 |
[Java] 반복문 (0) | 2022.03.07 |
댓글