[JAVA] 다형성 파헤쳐보기

2025. 10. 15. 10:52·JAVA

이전에 객체지향의 4가지 특성에 대해 정리해본적이 있다. 그 중에서도 다형성에 대해서 다시 한번 정리할 필요에 따라 글을 적어 가보고자 한다.

 

💡 왜 만들어졌는가?

기존에 알고있던 구현방식을 동물의 소리를 출력하는 과정을 예시로 들어 보자면

 

public class AnimalManager {
	public static void main(String[] args) {
    	Dog dog = new Dog("바둑이");
        Cat cat = new Cat("나비");
        Bird bird = new Bird("짹이");
        
        // 각각 따로따로 처리해야 함
        dog.makeSound();
        cat.makeSound();
        bird.makeSound();
        }
}

 

새로운 동물이 추가될 때마다 코드를 일일히 추가해줘야 하는 부분과, 반복문으로 한번에 처리할 수 없고 일일히 추가되기 때문에 코드가 엄청나게 길어지는 상황이 생기게 된다. 이를 해결하기 위해 다형성의 개념과 활용으로 문제를 보다 쉽게 풀어나아갈 수 있다.

 

💡 업캐스팅 : 부모 타입으로 자식 객체를 참조하기

 : 공통된 부분을 부모로 묶어 객체를 여러 타입으로 만들 수 있음

 

부모 클래스 만들기

class Animal {
	String name;
    
    Animal(String name) {
    	this.name = name;
    }
    
    void makeSound() {
    	System.out.println("동물 소리~");
    }
}

class Dog extends Animal {
	Dog(String name) {
    	super(name);
    }
    
    @Override
    void makeSound() {
    	System.out.println(name + ": 멍멍!");
    }
}

class Cat extends Animal {
	Cat(String name) {
    	super(name);
    }
    
    @Override
    void makeSound() {
    	System.out.println(name + ": 야옹~");
    }
}

class Bird extends Animal {
	Bird(String name) {
    	super(name);
    }
    
    @Override
    void makeSound() {
    	System.out.println(name + ": 짹짹!");
    }
}

 

생성 이후의 변화는 아래와 같다.

public class PolymorphismTest {
	public static void main(String[] args) {
    	//부모 타입으로 자식 객체를 담을 수 있다!
        Animal animal1 = new Dog("바둑이");
        Animal animal1 = new Dog("나비");
        Animal animal1 = new Dog("짹이");
        
        // 아래와 같이 가능해진다!
        System.out.pirntln("타입 확인:");
        System.out.pirntln("animal1 instanceof Animal"); // true
		System.out.pirntln("animal1 instanceof Dog"); // true
    }
}
  • Animal animal = new Dog(); 는 문법적으로 이상이 없음
  • 자식은 부모의 모든 것을 가지고 있으므로 부모 타입에 담을 수 있음

💡 오버라이딩 : 런타임 메서드 결정

같은 메서드 호출, 다른 결과를 도출

public class PolymorphismMagic {
	public static void main(String[] args) {
    	Animal animal1 = new Dog("바둑이");
        Animal animal2 = new Cat("야옹이");
        Animal animal3 = new Bird("짹이");
        
        // 모두 Animal 타입이지만
        animal1.makeSound(); // 바둑이: 멍멍!
        animal2.makeSound(); // 야옹이: 야옹~
        animal3.makeSound(); // 짹이: 짹짹!
       }
}
  • 주의깊게 확인해야 할 부분
    • 변수 타입은 모두 Animal
    • 하지만 실행되는 메서드는 실제 객체(Dog, Cat, Bird)의 메서드
      이런 과정이 다형성의 특징

배열로 한번에 처리

public class ArrayPolymorphism {
    public static void main(String[] args) {
        // 배열에 담을 수 있다!
        Animal[] animals = new Animal[3];
        anmals[0] = new Dog("바둑이");
        anmals[1] = new Dog("야옹이");
        anmals[2] = new Dog("짹이");

        System.out.println("=== 모든 동물의 소리 ===");
        for (Animal animal : animals) {
            animal.makeSound();
        }
        
        //출력:
        //바둑이: 멍멍!
        //나비: 야옹~
        //짹이: 짹짹!
    }
}

👉 다형성을 잘 활용만 한다면 비즈니스 로직은 유지한 채로 새로운 객체를 추가하는 것만으로도 전반적인 기능구현을 그대로 추가할 수 있다는 것!

 

메서드 매개변수로도 활용

public class PolymorphismParameter {
    // 모든 동물을 받을 수 있는 메서드
    public static void checkAnimal(Animal animal) {
        System.out.println(animal.name + "건강검진 중...");
        animal.makeSound();
        System.out.println("검진 완료!\n");
    }

    public static void main(String[] args) {
        Dog dog = new Dog("바둑이");
        Cat cat = new Cat("야옹이");
        Bird bird = new Bird("짹이");
        
        // 하나의 메서드로 모든 동물 처리!
        checkAnimal(dog);
        checkAnimal(cat);
        checkAnimal(bird);
    }
}

 

Before (다형성 없이)

public static void checkDog(Dog dog) {...}
public static void checkCat(Cat cat) {...}
public static void checkBird(Bird bird) {...}
// 동물 100종이면 메서드 100개를 작성해야 함

 

After (다형성 사용)

public static void checkAnimal(Animal animal) { ... }
// 끝! 모든 동물 처리 가능

 

💡다형성의 정의와 원리

다형성이란(Polymorphism) 이란?

Poly(많은) + Morph(형태) = 여러 형태를 가진다.

Animal animal = new Dog("바둑이");
  • 이 변수는 Animal 타입이기도 하고
  • 동시에 Dog 객체이기도 함
  • 하나의 객체가 여러 타입으로 취급될 수 있다.

다형성의 3가지 조건

1. 상속 관계(extends)

2. 메서드 오버라이딩 (@Override)

3. 부모 타입으로 자식 객체 참조

 

💡 다형성의 장점

장점1

새로운 동물 추가하기:

// 새로운 동물 추가
class Rabbit extends Animal {
	Rabbit(String name) {
    	super(name);
    }
    
    @Override
    void makeSound() {
    	System.out.println(name + ": 깡총깡총!");
    }
}

public clss ExtensibilityTest {
	public static void main(String[] args) {
    	Animal[] animals = {
        	new Dog("바둑이"),
            new Cat("야옹이"),
            new Bird("짹이"),
            new Rabbit("토순이") // 새로 추가
       };
       
       // 기존 코드 수정 없이 그대로 작동
       for(Animal animal : animals) {
       		animal.makeSound();
       		}
       }
}


👉 새로운 클래스만 만들면 되고 기존 코드는 건드릴 필요 없음

 

장점2 : 코드 중복 제거

Before(다형성 없이):

public class AnimalManager {
    List<Dog> dogs = new ArrayList<>();
    List<Cat> cats = new ArrayList<>();
    List<Bird> birds = new ArrayList<>();
    
    void addDog(Dog dog) { dogs.add(dog); }
    void addCat(Cat cat) { dogs.add(cat); }
    void addBird(Bird bird) { dogs.add(bird); }
    
    void soundAllDogs() {
        for (Dog dog : dogs) dog.makeSound();
    }
    void soundAllCats() {
        for (Cat cat : cats) cat.makeSound();
    }
    void soundAllBirds() {
        for (Bird bird : birds) bird.makeSound();
    }
}

 

After (다형성 사용):

public class AnimalManager {
    List<Animal> animals = new ArrayList<>();

    void addAnimal(Animal animal) {
        animals.add(animal);
    }

    void soundAll() {
        for (Animal animal : animals) {
            animal.makeSound();
        }
    }
}

 

Before vs After 비교 정리

항목 다형성 없이 다형성 사용
새 타입 추가 모든 관련 코드 수정 새 클래스만 추가
코드 중복 타입마다 메서드 필요 하나의 메서드로 통합
유지보수 어려움 (변경 영향 큼) 쉬움 (독립적)
코드 라인 수  많음 적음

 

💡 주의할 점들

1. 부모 타입으로는 자식 고유 메서드 호출 불가

class Dog extends Animal {
    Dog(String name) {
        super(name);
    }
    
    @Override
    void makeSound() {
        System.out.println(name + ": 멍멍!");
    }
    
    // Dog만의 고유 메서드
    void wagTail() {
        System.out.println(name + "가 꼬리를 흔듭니다!");
    }
}

    public class LimitationTest {
        public static void main(String[] args) {
            Dog dog = new Dog("바둑이");
            dog.wagTail(); // OK! Dog 타입이니까
            
            Animal animal = new Dog("뭉치");
            // animal.wagTail(); // 컴파일 에러!
            // Animal 타입에는 wagTail() 메서드가 없음
            
            animal.makeSound(); // OK! Animal에 있는 메서드
        }
    }

👉 이유

  • 컴파일러는 변수의 선언된 타입(Animal)만 봄
  • Animal 클래스에 wagTail()이 없으면 호출 불가
  • 실제 객체가 Dog여도 상관없음!

2. 형변환(Casting)이 필요한 경우

public class CastingExample {
    public static void main(String[] args) {
        Animal animal = new Dog("바둑이");
        
        // 자식 고유 메서드를 호출하려면 형변환 필요
        if (animal instanceof Dog) {
            Dog dog = (Dog) animal; // 다운캐스팅
            dog.wagTail(); // 이제 가능!
        }
        
        // 더 간단하게 (Java 16+)
        if (animal instanceof Dog dog) {
            dog.wagTail();
        }
    }
}

 

3. 잘못된 형변환은 런타임 에러!

public class WrongCasting {
    public static void main(String[] args) {
        Animal animal = new Cat("나비");
        
        // 위험한 코드!
        // Dog dog = (Dog) animal;  // ClassException 발생!
        
        // 안전하게 확인하고 반환
        if (animal instanceof Dog) {
            Dog dog = (Dog) animal;
            dog.wagTail();
        } else {
            System.out.println("이 동물은 Dog가 아닙니다!");
        }
    }
}

 

4. 업캐스팅 vs 다운캐스팅

public class CastingTypes {
    public static void main(String[] args) {
        //업캐스팅 (자동, 안전)
        Dog dog = new Dog("바둑이");
        Animal animal = dog; // 자동 변환, 괄호 불필요
        
        //다운캐스팅 (수동,위험)
        Animal animal2 = new Dog("뭉치");
        Dog dog2 = (Dog) animal2; // 명시적 변환 필요
        
        //잘못된 다운캐스팅
        Animal animal3 = new Cat("나비");
        // Dog dog3 = (Dog) animal3; // 런타임 에러!
    }
}

 

⭐️ 기억할 것!

  • 업캐스팅 (자식→부모): 항상 안전, 자동 변환
  • 다운캐스팅 (부모→자식): 위험할  수 있음, 반드시 instanceof로 확인!

핵심 정리

다형성의 핵심 3줄 요약

1. 부모 타입으로 자식 객체를 다룰 수 있다.

2. 같은 메서드 호출이 객체에 따라 다르게 동작한다.

3. 코드의 유연성과 확장성이 크게 향상된다.

 

반드시 기억할 것

  • ✅ 업캐스팅은 항상 안전
  • ⚠️ 다운캐스팅은 instanceof로 먼저 확인
  • ✅ 오버라이딩된 메서드는 실제 객체 기준으로 실행
  • ⚠️ 부모 타입으로는 자식 고유 메서드 호출 불가

 

'JAVA' 카테고리의 다른 글

[JAVA] 불변객체(Immutable Object)  (0) 2025.10.13
[JAVA] 예외처리  (1) 2025.10.13
[JAVA Algorithm] 컬렉션 프레임워크  (0) 2025.10.02
[Java Algorithm] 2차원 배열  (0) 2025.10.02
[JAVA Algorithm] 배열 개념 정리  (0) 2025.10.01
'JAVA' 카테고리의 다른 글
  • [JAVA] 불변객체(Immutable Object)
  • [JAVA] 예외처리
  • [JAVA Algorithm] 컬렉션 프레임워크
  • [Java Algorithm] 2차원 배열
stark77
stark77
하마의 IT 자기개발 이모저모, 백엔드 개발자로 거듭나기
  • stark77
    하마의 개발자 성장일기
    stark77
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 컴퓨터구조와 운영체제
        • 컴퓨터구조
        • 운영체제
      • SQL 기초
      • Spring
        • 백엔드 기초
        • Spring 실습
      • JAVA
        • Java 실습
      • HTML&CSS
        • HTML&CSS 실습
      • Git&GitHub
        • Git&GitHub 실습
      • 내배캠 끄적끄적
        • Today I Learned
      • 유용한 툴 및 사이트 정리
      • 취미
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    백엔드 기초
    Spriingboot
    MVC
    경합조건과 교착상태
    WebSocket
    실시간 데이터 처리
    Github
    객체지향
    RestTemplate
    BEAN
    git
    HTML&CSS
    JPA
    Stomp
    객체지향프로그래밍
    jsp
    algorithm
    웹소켓
    thymleaf
    String.format
    네트워크 기초
    Java 문법기초
    Spring
    java
    백엔드 기초다지기
    Til
    다형성
    프로세스와 쓰레드
    for문
    SpringSecurity
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
stark77
[JAVA] 다형성 파헤쳐보기
상단으로

티스토리툴바