자바 인터페이스와 다형성 쉽게 정리하기
💡 인터페이스로 다형성을 구현하는 방법
자바에서 다형성을 실현하려면 두 가지가 필요합니다:
- 메소드 재정의 (오버라이딩)
- 타입 변환 (업캐스팅/다운캐스팅)
이 두 가지는 상속에서뿐만 아니라 인터페이스를 통해서도 구현할 수 있습니다.
🔁 인터페이스 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 (의존성 주입): 스프링 등에서 인터페이스로 의존성 설정
- 전략 패턴: 동작을 외부에서 주입 가능
- 유지보수성 향상: 클래스 변경 없이 기능 교체 가능
'자바' 카테고리의 다른 글
자바 익명 객체 완전 정리 (0) | 2025.06.05 |
---|---|
Java 중첩 클래스와 중첩 인터페이스 완전 정리 (1) | 2025.06.05 |
자바 인터페이스 완전 정리 (초보도 이해하는 구조 중심 해설) (0) | 2025.06.04 |
자바 추상 클래스 완전 정리 (1) | 2025.06.04 |
자바 타입 변환과 다형성 완전 정복 (2) | 2025.06.04 |