1. 기본 개념
인터페이스 (Interface)
- 객체가 어떤 동작을 할 수 있는지를 정의하는 계약(Contract)
- 메서드의 시그니처(이름, 매개변수, 반환 타입)만 선언하며 구현은 제공하지 않음
(Kotlin에서는 디폴트 메서드 구현도 가능함.) - 클래스는 여러 개의 인터페이스를 구현(implement) 가능
interface Flyable {
fun fly()
}
class Bird : Flyable {
override fun fly() {
println("Bird is flying")
}
}
상속 (Inheritance)
- 객체 간의 is-a 관계를 정의
- 상속받은 클래스는 부모 클래스의 속성과 메서드를 사용할 수 있으며, 필요하면 재정의(override) 가능
- 단일 상속만 허용 (Kotlin은 클래스는 하나만 상속받을 수 있음)
open class Animal {
open fun eat() {
println("Animal is eating")
}
}
class Dog : Animal() {
override fun eat() {
println("Dog is eating")
}
}
2. 주요 차이점
특징 | 인터페이스 | 상속 |
목적 | 동작(행위)의 규약 정의 | 공통된 속성과 동작의 상속 |
구현 여부 | 메서드 구현은 선택적(Default Method 가능) | 부모 클래스의 구현 상속 가능 |
다중 사용 | 다중 인터페이스 가능 | 단일 상속 가능 |
is-a 관계 | 객체 간의 행위에 초점 | 객체 간의 is-a 관계 나타냄 |
구조적 결합 | 느슨한 결합 (구현부와 독립접) | 강한 결합 (부모/자식 클래스 밀접 연관) |
속성 | 프로퍼티는 추상적인 선언 가능 ( 구현 x) | 부모클래스 속성을 그대로 상속 가능 |
사용 예시 | 행위 정의하고 여러 클래스 공유 | 공통 속성과 동작을 공유하는 계층적인 구조에서 사용 |
3. 언제 인터페이스를 사용하고 언제 상속을 사용할까?
인터페이스를 사용할 때
- 클래스 간의 공통 행위를 정의할 때
- 예: Flyable, Swimable 같은 역할(행위)을 나타낼 때
- 다중 구현이 필요할 때
- Kotlin에서는 여러 인터페이스를 구현할 수 있지만, 상속은 하나만 가능.
- 의존성을 낮추고 유연성을 높이고 싶을 때.
상속을 사용할 때
- 클래스 간의 is-a 관계가 명확할 때
- 예: Dog는 Animal이다
- 공통 속성과 동작을 공유하고자 할 때
- 부모 클래스에서 정의된 로직을 재사용
클래스와 인터페이스의 동시 사용
클래스와 인터페이스를 조합하여 사용가능
open class Animal {
open fun eat() {
println("Animal is eating")
}
}
interface Swimable {
fun swim() {
println("Swimming in water")
}
}
class Duck : Animal(), Swimable {
override fun eat() {
println("Duck is eating")
}
}
결론
- 인터페이스는 동작을 정의하는 데 적합하며, 다중 구현이 필요한 경우에 사용
- 상속은 공통 속성과 동작을 공유할 때 적합하며, 명확한 is-a 관계가 있을 때 사용
- "구현 상속보다 인터페이스를 선호하라"는 객체지향 설계의 권장사항
하지만 특정 상황에서는 상속이 더 적합할 수 있음