Java 다형성 완벽 가이드

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 연산자를 사용하고, 적절한 타입 캐스팅을 통해 자식 클래스의 고유 기능에 접근할 수 있습니다. 이를 통해 다형성의 유연성을 유지하면서도 안전하게 자식 클래스의 기능을 활용할 수 있습니다.

태그: java OOP Polymorphism Inheritance Object-Oriented

8월 30일 10:26에 게시됨