Java 다형성
1. 다형성이란?
동일한 객체가 상황에 따라 다른 형태로 나타나는 것을 의미합니다. 예를 들어, 사람은 직관적으로는 한 사람이지만, 환경에 따라 개발자, 학생, 자녀 등 다양한 역할을 수행할 수 있습니다.
다형성이成立하기 위한 조건:
상속 또는 구현 관계가 존재해야 합니다. 메서드 오버라이딩이 이루어져야 하며, 부모 클래스 레퍼런스가 자식 클래스 객체를 참조해야 합니다.
기본 구현 예제:
package poly.example;
public abstract class Entity {
public abstract void act();
}
package poly.example;
public class Robot extends Entity {
@Override
public void act() {
System.out.println("로봇이 움직입니다");
}
}
package poly.example;
public class Main {
public static void main(String[] args) {
Entity entity = new Robot();
entity.act();
}
}
2. 다형성에서 멤버 접근 특성
멤버 변수의 경우: 컴파일 시와 런타임 시 모두左侧(부모 클래스)을 기준으로 동작합니다.
멤버 메서드의 경우: 컴파일 시는左侧(부모 클래스)을 확인하고, 런타임 시는右侧(실제 객체)의 메서드를 실행합니다.
이러한 차이는 메서드는 오버라이딩이 가능하지만 변수는 오버라이딩되지 않기 때문입니다.
package poly.member;
public class Vehicle {
public int speed = 100;
public void run() {
System.out.println("차량이 달립니다");
}
}
package poly.member;
public class Car extends Vehicle {
public int speed = 200;
public String brand = "현대";
@Override
public void run() {
System.out.println("자동차가 빠르게 달립니다");
}
public void honk() {
System.out.println("자동차 경적이 울립니다");
}
}
package poly.member;
public class Test {
public static void main(String[] args) {
Vehicle vehicle = new Car();
System.out.println(vehicle.speed); // 100 출력 (부모 클래스 기준)
vehicle.run(); // "자동차가 빠르게 달립니다" 출력 (오버라이딩된 메서드)
}
}
3. 다형성의 장점과 단점
장점: 코드의 확장성이 향상됩니다. 메서드 정의 시 부모 타입을 파라미터로 사용하면,运行时에 다양한 자식 타입을 전달하여 처리할 수 있습니다. 이는 새로운 클래스가 추가되더라도 기존 코드를 수정하지 않고 확장할 수 있음을 의미합니다.
단점: 자식 클래스에만 존재하는 고유 기능을 사용할 수 없습니다. 다형성은 부모 레퍼런스를 통해 메서드를 호출하므로, 자식 클래스에서 새롭게 추가한 메서드는 접근이 불가능합니다.
package poly.advantage;
public class ShapeDrawer {
public void draw(Shape shape) {
shape.render();
// shape.animate(); // Shape 클래스에 없으면 호출 불가
}
}
package poly.advantage;
public abstract class Shape {
public abstract void render();
}
package poly.advantage;
public class Circle extends Shape {
@Override
public void render() {
System.out.println("원을 그립니다");
}
public void animate() {
System.out.println("원이 회전합니다");
}
}
package poly.advantage;
public class Rectangle extends Shape {
@Override
public void render() {
System.out.println("사각형을 그립니다");
}
}
package poly.advantage;
public class Demo {
public static void main(String[] args) {
ShapeDrawer drawer = new ShapeDrawer();
drawer.draw(new Circle());
drawer.draw(new Rectangle());
}
}
4. 다형성에서의 타입 변환
업캐스팅 (업ward Casting): 자식 타입에서 부모 타입으로 변환하는 것으로, 자동적으로 이루어집니다. 다형성에서 부모 레퍼런스가 자식 객체를 참조하는 것이 대표적인 예시입니다.
다운캐스팅 (다운ward Casting): 부모 타입에서 자식 타입으로 변환하는 것으로, 명시적 타입 캐스팅이 필요합니다. 이때 실제 객체 타입과 일치하지 않으면 ClassCastException이 발생합니다.
package poly.casting;
public class Person {
public void speak() {
System.out.println("사람이 말합니다");
}
}
package poly.casting;
public class Teacher extends Person {
@Override
public void speak() {
System.out.println("선생님이 수업을 합니다");
}
public void teach() {
System.out.println("학생들을 가르칩니다");
}
}
package poly.casting;
public class Main {
public static void main(String[] args) {
Person person = new Teacher();
person.speak();
Teacher teacher = (Teacher) person;
teacher.teach();
}
}
5. 실전 프로젝트: 동물 병원 시스템
다형성을 활용하여 동물 병원 예약 시스템을 구현해 보겠습니다. 동물이라는 추상화 개념을 두고, 구체적인 동물들이 이를 상속받아 자신만의 치료 메서드를 구현합니다.
package vet.hospital;
public abstract class Animal {
private String name;
private int age;
public Animal() {}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public abstract void receiveTreatment();
public void introduce() {
System.out.println("이름: " + name + ", 나이: " + age);
}
// Getter 및 Setter
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
package vet.hospital;
public class Cat extends Animal {
public Cat() {}
public Cat(String name, int age) {
super(name, age);
}
@Override
public void receiveTreatment() {
System.out.println("고양이에게 예방접종을 합니다");
}
public void climb() {
System.out.println("고양이가 나무를 올라갑니다");
}
}
package vet.hospital;
public class Dog extends Animal {
public Dog() {}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void receiveTreatment() {
System.out.println("강아지에게 건강검진을 합니다");
}
public void fetch() {
System.out.println("강아지가 공을 가져옵니다");
}
}
package vet.hospital;
public class Bird extends Animal {
public Bird() {}
public Bird(String name, int age) {
super(name, age);
}
@Override
public void receiveTreatment() {
System.out.println("새에게wings 검사를 합니다");
}
public void fly() {
System.out.println("새가 날아다닙니다");
}
}
package vet.hospital;
public class VetClinic {
public void treat(Animal animal) {
animal.receiveTreatment();
}
}
package vet.hospital;
public class ReservationSystem {
public static void main(String[] args) {
VetClinic clinic = new VetClinic();
Animal patient1 = new Cat("나비", 3);
Animal patient2 = new Dog("초코", 5);
Animal patient3 = new Bird("사랑", 2);
clinic.treat(patient1);
clinic.treat(patient2);
clinic.treat(patient3);
}
}
다형성 활용 시 주의사항
객체의 실제 타입을 확인해야 하는 경우 instanceof 연산자를 사용하고, 적절한 타입 캐스팅을 통해 자식 클래스의 고유 기능에 접근할 수 있습니다. 이를 통해 다형성의 유연성을 유지하면서도 안전하게 자식 클래스의 기능을 활용할 수 있습니다.