프로그래밍자바 개발자

자바에서 다형성이란 무엇이며, 어떻게 구현되며, 왜 필요한가?

Hintsage AI 어시스턴트로 면접 통과

답변.

질문 역사:

다형성은 객체 지향 프로그래밍 (OOP)의 핵심 원칙 중 하나로, 자바가 처음부터 지원해온 기능입니다. 이는 프로그램 실행 중 객체의 실제 유형에 따라 다르게 행동할 수 있게 해주며, 기본 유형의 참조를 사용하더라도 가능합니다.

문제:

다형성이 없으면 코드는 유연하지 않고, 서로 다른 유형의 객체를 다루기 위해 switch-case 또는 if-else와 같은 중복된 구조가 자주 나타납니다. 이는 코드의 유지보수 및 확장을 어렵게 만듭니다. 다형성은 서로 다른 유형에 대해 반복적인 코드를 작성할 필요를 해결합니다.

해결책:

자바의 다형성은 상속과 인터페이스를 통해 구현됩니다. 이는 다음을 가능하게 합니다:

  • 부모 유형의 참조가 자식 객체를 가리킬 수 있도록;
  • 컴파일 시 객체의 구체적인 유형에 대한 지식 없이 오버라이드된 메소드를 호출할 수 있도록.

코드 예시:

class Animal { void speak() { System.out.println("Animal speaks"); } } class Dog extends Animal { @Override void speak() { System.out.println("Dog barks"); } } class Cat extends Animal { @Override void speak() { System.out.println("Cat meows"); } } public class PolyDemo { public static void main(String[] args) { Animal a1 = new Dog(); Animal a2 = new Cat(); a1.speak(); // Dog barks a2.speak(); // Cat meows } }

주요 특징:

  • 기존 코드를 수정하지 않고 시스템을 확장하고 수정할 수 있습니다.
  • 구성 요소 간의 느슨한 결합을 보장합니다.
  • 다형성을 통해 디자인 패턴이 구현됩니다 (예: Strategy, Command).

함정 질문.

다형성에서 메소드 오버로딩(Overloading)과 오버라이딩(Overriding)의 차이점은 무엇인가?

오버로딩은 하나의 클래스 내에서 같은 이름의 메소드를 여러 개 정의하되, 시그니처가 다른 경우를 말합니다. 오버라이딩은 서브클래스에서 부모 클래스의 메소드와 같은 시그니처로 메소드를 정의하는 것을 의미합니다.

class Example { void foo(int x) {} void foo(String y) {} // 이것은 오버로딩 } class Base { void foo() {} } class Child extends Base { @Override void foo() {} // 이것은 오버라이딩 }

상속 없이 다형성이 있을 수 있는가?

전통적인 의미에서 자바에서는 아닙니다: 다형성은 상속 계층이나 구현된 인터페이스가 필요합니다.

부모 클래스 참조로 서브클래스 메소드를 호출할 수 있는가?

부모 클래스에서 정의된 메소드 또는 서브클래스에서 오버라이드된 메소드만 호출할 수 있습니다. 서브클래스에만 있는 메소드는 형변환 없이는 호출할 수 없습니다.

Animal a = new Dog(); a.speak(); // 가능 // a.fetch(); // 컴파일 오류, Dog가 fetch() 메소드를 가졌더라도

일반적인 오류 및 안티 패턴

  • @Override 어노테이션 없이 메소드를 오버라이드 — 컴파일러가 시그니처 불일치를 감지하지 못함.
  • instanceof를 통한 검증 없이 형변환 사용하기, 이는 ClassCastException을 발생시킴.
  • 조합으로 해결할 수 있는 곳에서 다형성을 사용하는 것.

일상적인 예시

부정적인 사례

프로젝트에서 Dog, Cat, Cow 클래스가 구현되었으나, 사용된 코드는 명시적 형변환 및 instanceof를 통해 타입과 직결되어 있습니다:

장점:

  • 새로운 동물 유형을 빠르게 구현할 수 있음.

단점:

  • if-else의 증가.
  • OOP 원칙 위반.
  • 확장성이 어려움.

긍정적인 사례

Animal을 상속받아 가상 speak()를 구현하고, 모든 상호작용은 기본 유형인 Animal을 통해 이루어집니다.

장점:

  • 새로운 유형 추가 시 유연함.
  • 테스트 용이.

단점:

  • 객체 간 전혀 연관성이 없으면 복잡해질 수 있으며, 이럴 땐 조합이 더 바람직할 수 있음.