자바

자바 인터페이스와 다형성 쉽게 정리하기

a-bell2 2025. 6. 5. 12:30

자바 인터페이스와 다형성 쉽게 정리하기

💡 인터페이스로 다형성을 구현하는 방법

자바에서 다형성을 실현하려면 두 가지가 필요합니다:

  • 메소드 재정의 (오버라이딩)
  • 타입 변환 (업캐스팅/다운캐스팅)

이 두 가지는 상속에서뿐만 아니라 인터페이스를 통해서도 구현할 수 있습니다.


🔁 인터페이스 vs 상속의 다형성 차이

구분  상속  인터페이스
개념 같은 종류의 하위 클래스를 만든다 동일한 사용 방법을 제공한다
공통점 메소드 재정의와 타입 변환을 통해 다형성 구현  
차이점 단일 상속만 가능 다중 상속 가능 (여러 인터페이스 상속 가능)

🔷 자동 타입 변환 (업캐스팅)

인터페이스를 구현한 객체는 자동으로 인터페이스 타입으로 변환할 수 있습니다. 즉, 다음과 같은 코드가 가능해요:

interface Animal {
    void sound();
}

class Dog implements Animal {
    public void sound() {
        System.out.println("멍멍!");
    }
}

Animal a = new Dog();  // ✅ 자동 타입 변환

이렇게 하면 Dog 객체는 Animal 타입 변수로 참조할 수 있게 됩니다.


🧠 왜 인터페이스 타입으로 받을까?

Animal a = new Dog();

이렇게 작성하면 Dog에 의존하지 않고, 나중에 다른 동물 클래스를 대입해도 코드 수정 없이 재사용 가능합니다. 예를 들어:

a = new Cat();  // 인터페이스만 같다면 가능

이게 바로 다형성입니다.


🔷 필드에 인터페이스를 사용하면?

클래스 내부 필드에도 인터페이스 타입을 선언하면, 다양한 구현 객체를 유연하게 넣을 수 있습니다.

interface Speaker {
    void volumeUp();
}

class SamsungSpeaker implements Speaker {
    public void volumeUp() { System.out.println("삼성 ↑"); }
}

class TV {
    Speaker speaker;  // 인터페이스 타입

    TV(Speaker speaker) {
        this.speaker = speaker;
    }

    void volumeUp() {
        speaker.volumeUp();
    }
}
TV tv = new TV(new SamsungSpeaker());
tv.volumeUp(); // 삼성 ↑

→ 필드 타입은 그대로 두고 객체만 바꾸면 다양한 기능을 확장할 수 있습니다.


🔷 매개변수에 인터페이스를 사용하면?

메소드의 매개변수를 인터페이스 타입으로 선언하면, 다양한 객체를 받을 수 있습니다.

interface Speaker {
    void speak();
}

class Korean implements Speaker {
    public void speak() { System.out.println("안녕하세요!"); }
}

class American implements Speaker {
    public void speak() { System.out.println("Hello!"); }
}

class Greeting {
    void sayHello(Speaker speaker) {
        speaker.speak();
    }
}
Greeting g = new Greeting();
g.sayHello(new Korean());    // 안녕하세요!
g.sayHello(new American());  // Hello!

→ 메소드는 하나인데 다양한 결과가 나오는 게 바로 다형성이에요.


🔷 강제 타입 변환 (다운캐스팅)

자동 타입 변환된 객체는 인터페이스에 정의된 메소드만 사용할 수 있어요. 구현 클래스의 고유 메소드는 사용할 수 없습니다.

Animal a = new Dog();  // 자동 변환
a.sound();             // 가능
// a.wagTail();        // 오류! Animal에는 wagTail() 없음

필요하다면 강제로 타입 변환해서 사용할 수 있어요:

((Dog)a).wagTail();

또는 안전하게 instanceof를 사용할 수 있어요:

if (a instanceof Dog) {
    ((Dog)a).wagTail();
}

🔷 인터페이스의 다중 상속

인터페이스는 여러 인터페이스를 동시에 상속할 수 있습니다.

interface Flyable { void fly(); }
interface Swimable { void swim(); }

interface Amphibious extends Flyable, Swimable {
    void moveOnLand();
}
class Duck implements Amphibious {
    public void fly() { System.out.println("날아요"); }
    public void swim() { System.out.println("헤엄쳐요"); }
    public void moveOnLand() { System.out.println("걷기도 해요"); }
}

다음과 같이 다양한 타입으로 참조할 수 있습니다:

Amphibious a = new Duck();  // 모든 메소드 사용 가능
Flyable f = new Duck();     // fly()만 사용 가능
Swimable s = new Duck();    // swim()만 사용 가능

✅ 핵심 요약

개념  설명
자동 타입 변환 구현 객체 → 인터페이스 타입으로 변환
필드 다형성 필드를 인터페이스 타입으로 선언하여 다양한 객체 주입
매개변수 다형성 메소드 매개변수에 인터페이스 사용
강제 타입 변환 다시 구현 객체로 변환해야 고유 기능 사용 가능
인터페이스 상속 여러 인터페이스를 한 번에 상속 가능 (다중 상속 허용)

📌 실제 개발에서 어떻게 쓰이나요?

  • DI (의존성 주입): 스프링 등에서 인터페이스로 의존성 설정
  • 전략 패턴: 동작을 외부에서 주입 가능
  • 유지보수성 향상: 클래스 변경 없이 기능 교체 가능