본문 바로가기
Programming/Java

[Java] 다형성

by AI_Wooah 2022. 3. 7.

다형성이란?

'서로 다른 형태'를 지칭하는 말로 객체지향 프로그래밍의 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

댓글