다중 상속과 유사한 interface

순순·2024년 4월 2일

코틀린

목록 보기
5/8

추상클래스와 인터페이스가 굉장히 헷갈리기에 개념도 잡을 겸 오버라이딩과 인터페이스에 대해 정리해보았다.

overriding


  • 상속 관계에서의 이야기.
  • 부모가 가지고 있는 메서드를 다시 구현하는 개념

특징

  • 매개변수의 형태, 이름, 반환값 등 모든 것이 같아야 한다.
  • override 키워드를 붙여줘야 한다.

예시

// 상속받을 부모클래스
open class SuperClass1{
    val superValue1 = 100

    fun superMethod1(){
        println("SuperClass1의 Method1")
    }

    fun superMethod2(){
        println("SuperClass2의 Method2")
    }
}

// 부모클래스 상속받는 자식클래스
// 여기서 SuperClass1 뒤에 ()를 넣어줘야 한다. (안넣으면 오류남)
// constructor를 따로 지정하지 않아서 자동으로 생성되었기 때문
class SubClass1 : SuperClass1(){
    val subValue1 = 200

    fun subMethod1(){
        println("SubClass1의 메서드")
    }

    fun subMethod2(){
        println("SubClass2의 SubMethod1")
    }

}

Interface


  • 객체가 가진 기능 혹은 동작을 명시해놓은 것
  • 기능명세서 라고도 부른다.
    • 객체가 어떤 동작을 수행할 수 있도록 정의함.
    • 실제로 이 동작을 구현하는 책임은 인터페이스를 구현하는 클래스에 있다.
    • 클래스가 이 명세를 따르도록 강제함.

배경

Kotlin, Java 등은 다중 상속을 지원하지 않는다. 따라서 상속을 통해서는 자기 타입의 변수 or 부모형 타입의 변수에만 값을 담을 수 있다.

이러한 상속의 단점을 보완하기 위해 나온 것이 interface.
때문에 인터페이스는 다중 상속과 유사한 의미라고 하는 것이다.

예시

자동차를 인터페이스 구현 예시로 생각하면 쉽다. 우리는 자동차의 동작 원리에 대해 몰라도 자동차를 운전할 수 있다.

엔진 내부의 동작 원리나 브레이크 시스템의 작동 방식을 몰라도, 핸들, 페달, 기어와 같은 조작 방법만 알면 충분히 운전할 수 있다는 뜻이다.

이처럼 인터페이스 개념에서는 사용자가 자동차의 구체적인 기술 구현에 대해 알 필요 없이, 정해진 동작(인터페이스)을 통해 자동차를 운전할 수 있음을 의미한다.

정리하자면, 인터페이스는 사용자와 시스템 간의 추상화된 약속을 제공하여 내부 구현과 상관없이 필요한 기능을 이용할 수 있게 해준다는 것. 참 편리한 기능이다.

  • 동작에 대한 명세 ex) 주행, 멈춤, 방향 전환 등
  • 기능에 대한 명세 ex) 연료 상태 확인, 엔진 시동, 에어컨 조절 등

참고

= interface
= inter + face
= face to face
= ‘상호작용’이라는 의미

코드를 통해 살펴보자

// 자동차의 동작에 대한 인터페이스
interface Car {
    fun start() // 시동을 켠다
    fun drive() // 주행한다
    fun stop()  // 멈춘다 
}

// 인터페이스를 마치 상속처럼 받아서 구현한다
class Sedan : Car {
    override fun start() {
        // Sedan의 시동 켜기 구현
    }

    override fun drive() {
        // Sedan 주행 구현
    }

    override fun stop() {
        // Sedan 정지 구현
    }
}

class SUV : Car {
    override fun start() {
        // SUV의 시동 켜기 구현
    }

    override fun drive() {
        // SUV 주행 구현
    }

    override fun stop() {
        // SUV 정지 구현
    }
}

Interface는 클래스가 아니다


나는 인터페이스를 처음 접했을 때, 대충 추상클래스와 동일한 개념이구나 하고 이해해서 그 둘의 개념이 혼동되었다. 그래서 둘의 공통점과 차이점을 찾아서 정리해 보았다.


인터페이스와 추상 클래스의 공통점

  • 둘 다 객체를 생성할 수 없다.
  • 둘 다 필요한 기능을 정의만 하고 구체적인 구현은 제공하지 않는다.

인터페이스와 추상 클래스의 차이점

  • 인터페이스는 "어떤 메서드가 꼭 있어야 한다"라는 선언만 작성할 수 있고, 메서드 안에 구체적인 내용은 포함하지 않는다. (인터페이스를 implement 하는 클래스에서 실제로 동작할 내용을 채워넣는다)
  • 추상 클래스는 인터페이스와 달리 구체적인 메서드 구현과 상태를 포함할 수 있다. 공통 기능이나 기본 동작을 추상 클래스에 미리 구현해두고, 필요한 부분만 하위 클래스에서 재정의 할 수 있다.

추상 클래스 예시 코드

아래 코드에서 추상 클래스 Animal은 기본 동작으로 sleep() 메서드를 구현하고 있지만, sound() 메서드는 추상 메서드로 선언되어 하위 클래스가 반드시 구현해야 한다. 아까 위에서 보았던 인터페이스의 코드와는 확연이 다른 차이점이 보인다.

abstract class Animal {
    fun sleep() {
        println("Sleeping...")
    }

    abstract fun sound()  // 이 메서드는 구현이 필요하다고만 선언
}

class Dog : Animal() {
    override fun sound() {
        println("Bark")
    }
}

fun main() {
    val dog = Dog()
    dog.sleep()  // 출력: Sleeping...
    dog.sound()  // 출력: Bark
}
profile
플러터와 안드로이드를 공부합니다

0개의 댓글