다형성
객체지향 프로그래밍의 특징 중에는 다형성(Polymorphism)이 있다.
interface Carnivore{
String getFood();
default void printFeed() {
System.out.printf("my food is %s\n", getFood());
}
int LEG_COUNT = 4;
static int speed() {
return LEG_COUNT * 30;
}
}
class Animal {
String name;
void setName(String name) {
this.name = name;
}
}
class Tiger extends Animal implements Carnivore{
public String getFood() {
return "apple";
}
}
class Lion extends Animal implements Carnivore{
public String getFood() {
return "banana";
}
}
class Crocodile extends Animal implements Carnivore{
public String getFood() {
return "watermelon";
}
}
class ZooKeeper{
void feed(Carnivore carnivore) {
System.out.println("feed " + carnivore.getFood());
}
}
class Growl{
void barkAnimal(Animal animal) {
if (animal instanceof Tiger) {
System.out.println("어흥");
} else if (animal instanceof Lion) {
System.out.println("으르렁");
}
}
}
public class TestClass{
public static void main(String []args) {
// ZooKeeper zooKeeper = new ZooKeeper();
Tiger tiger = new Tiger();
Lion lion = new Lion();
Growl growl = new Growl();
growl.barkAnimal(tiger); // 어흥
growl.barkAnimal(lion); // 으르렁
}
}
class Growl{ void barkAnimal(Animal animal) { if (animal instanceof Tiger) { System.out.println("어흥"); } else if (animal instanceof Lion) { System.out.println("으르렁"); } } }
Tiger tiger = new Tiger(); Lion lion = new Lion(); Growl growl = new Growl(); growl.barkAnimal(tiger); // 어흥 growl.barkAnimal(lion); // 으르렁
barkAnimal 메서드는 입력으로 받은 animal 객체가 Tiger 객체인 경우 "어흥"을 출력하고
Lion 객체인 경우 "으르렁"을 출력한다. instanceof 는 어떤 객체가 특정 클래스의 객체인지를 조사할 때
사용되는 자바의 내장 명령어이다.
animal instanceof Tiger 는 "animal 객체는 Tiger 클래스로 만들어진 객체인가?"를 묻는 조건문이다.
IS-A 관계
barkAnimal 메서드의 입력자료형은 Tiger나 Lion이 아닌 Animal 이다.
하지만 barkAnimal 메서드를 호출할 때 tiger 또는 lion 객체를 전달할 수가 있다.
이게 가능한 이유는 Tiger, Lion 클래스가 Animal이라는 부모 클래스를 상속한 자식 클래스이기 때문이다.
자식 클래스에 의해서 만들어진 객체는 언제나 부모 클래스의 자료형으로 사용할 수가 있다.
즉, 다음과 같은 코딩이 가능하다.
Animal tiger = new Tiger(); // Tiger is a Animal Animal lion = new lion(); // Lion is a Animal
하지만 위와 같은 방식의 구조는 동물 클래스가 추가될 때마다 분기문이 추가되야 하므로 바람직하지 않다.
따라서 인터페이스를 통해 이를 변경할 수 있다.
interface Carnivore{
String getFood();
default void printFeed() {
System.out.printf("my food is %s\n", getFood());
}
int LEG_COUNT = 4;
static int speed() {
return LEG_COUNT * 30;
}
}
// +interface 추가
interface Barkable{
void bark();
}
class Animal {
String name;
void setName(String name) {
this.name = name;
}
}
// + Barkable
class Tiger extends Animal implements Carnivore, Barkable{
public String getFood() {
return "apple";
}
// class 내 method 추가
public void bark() {
System.out.println("어흥");
}
}
class Lion extends Animal implements Carnivore, Barkable{
public String getFood() {
return "banana";
}
public void bark() {
System.out.println("으르렁");
}
}
class Crocodile extends Animal implements Carnivore, Barkable{
public String getFood() {
return "watermelon";
}
public void bark() {
System.out.println("크롱");
}
}
class ZooKeeper{
void feed(Carnivore carnivore) {
System.out.println("feed " + carnivore.getFood());
}
}
class Growl{
// void barkAnimal(Animal animal) {
// if (animal instanceof Tiger) {
// System.out.println("어흥");
// } else if (animal instanceof Lion) {
// System.out.println("으르렁");
// }
// }
// interface 사용으로 인한 변경
// 조건분기문 삭제
void barkAnimal(Barkable animal) {
animal.bark();
}
}
인터페이스는 콤마( , )를 이용하여 여러개를 implements할 수 있다.
Tiger, Lion 클래스는 Carnivore 인터페이스와 Barkable 인터페이스를 implements 하였다.
따라서 Tiger, Lion 클래스는 bark 메서드를 위와 같이 구현해야 한다.
이렇게 Tiger, Lion 클래스에 bark 메서드를 구현하면 Growl 클래스의 barkAnimal 메서드를 위처럼 수정할 수 있다.
다형성을 이용하면 위의 예처럼 복잡한 형태의 분기문을 간단하게 처리할 수 있는 경우가 많다.
이렇게 하나의 객체가 여러개의 자료형 타입을 가질 수 있는 것을 객체지향에서 다형성(polymorphism)이라고 한다.
즉, Tiger 클래스의 객체는 다음과 같이 여러가지 자료형으로 표현할 수 있다.
Tiger tiger - new Tiger(); // Tiger is a Tiger
Animal animal = new Tiger(); // Tiger is a Animal
Carnivore carnivore = new Tiger(); // Tiger is a Carnivore
Barkable barkable = new Tiger(); // Tiger is a Barkable
여기서 알아두어야 할 사항은 Carnivore로 선언된 carnivore 객체와
Barkable 로 선언된 barkable 객체는 사용할 수 있는 메서드가 서로 다르다는 점이다.
Carnivore 객체는 getFeed() 메서드가 선언된 Carnivore 인터페이스의 객체이므로 getFeed 메서드만 호출이 가능하다.
이와 마찬가지로 Barkable로 선언된 barkable 객체는 bark 메서드만 호출이 가능하다.
만약 getFeed 메서드와 bark 메서드 모두를 사용하고 싶다면 어떻게 해야 할까
Carnivore, Barkable 인터페이스를 구현한 Tiger로 선언된 tiger 객체를 그대로 사용하거나
다음과 같이 getFeed, bark 메서드를 모두 포함하는 새로운 인터페이스를 새로 만들어 사용하면 된다.
interface Carnivore{
String getFood();
default void printFeed() {
System.out.printf("my food is %s\n", getFood());
}
int LEG_COUNT = 4;
static int speed() {
return LEG_COUNT * 30;
}
}
interface Barkable{
void bark();
}
interface MergeCNB extends Carnivore, Barkable{
}
기존의 인터페이스를 상속하여 MergeCNB 를 만들었다.
위와 같이하면 Carnivore의 getFood 메서드, Barkable의 bark 메서드를 그대로 사용할 수 있다.
인터페이스는 일반 클래스와는 달리 extends 를 이용하여 여러개의 인터페이스(Carnivore, Barkable)를
동시에 상속할 수 있다. 즉, 다중 상속이 지원된다. (※ 일반 클래스는 단일상속만 가능하다.)
Lion 클래스를 위에서 작성한 MergeCNB 인터페이스를 통해 구현하도록 수정해보자.
//class Lion extends Animal implements Carnivore, Barkable{
class Lion extends Animal implements MergeCNB{
public String getFood() {
return "banana";
}
public void bark() {
System.out.println("으르렁");
}
}
※ sample code:
package package_01;
interface Carnivore{
String getFood();
default void printFeed() {
System.out.printf("my food is %s\n", getFood());
}
int LEG_COUNT = 4;
static int speed() {
return LEG_COUNT * 30;
}
}
interface Barkable{
void bark();
}
interface MergeCNB extends Carnivore, Barkable{
}
class Animal {
String name;
void setName(String name) {
this.name = name;
}
}
// + Barkable
class Tiger extends Animal implements Carnivore, Barkable{
public String getFood() {
return "apple";
}
// class 내 method 추가
public void bark() {
System.out.println("어흥");
}
}
//class Lion extends Animal implements Carnivore, Barkable{
class Lion extends Animal implements MergeCNB{
public String getFood() {
return "banana";
}
public void bark() {
System.out.println("으르렁");
}
}
class Crocodile extends Animal implements Carnivore, Barkable{
public String getFood() {
return "watermelon";
}
public void bark() {
System.out.println("크롱");
}
}
class ZooKeeper{
void feed(Carnivore carnivore) {
System.out.println("feed " + carnivore.getFood());
}
}
class Growl{
// void barkAnimal(Animal animal) {
// if (animal instanceof Tiger) {
// System.out.println("어흥");
// } else if (animal instanceof Lion) {
// System.out.println("으르렁");
// }
// }
// interface 사용으로 인한 변경
void barkAnimal(Barkable animal) {
animal.bark();
}
}
public class TestClass{
public static void main(String []args) {
// ZooKeeper zooKeeper = new ZooKeeper();
Tiger tiger = new Tiger();
Lion lion = new Lion();
Growl growl = new Growl();
growl.barkAnimal(tiger); // 어흥
growl.barkAnimal(lion); // 으르렁
}
}
'Language > Java' 카테고리의 다른 글
Java - Access Modifier (0) | 2023.03.27 |
---|---|
Java - Abstract Class (0) | 2023.03.27 |
Java - Interface (0) | 2023.03.26 |
Java - Constructor (0) | 2023.03.26 |
Java - Inheritance (0) | 2023.03.26 |