티스토리 뷰

😶‍🌫️ Kotlin에서의 상속

  • Kotlin에서는 상속시 sub class에서 super class와 같은 이름의 함수를 만들 수는 없다.
  • 하지만 super class에서 허용만 한다면 overriding을 통해 sub class에서 같은 이름의 함수를 만들 수 있다.
  • 당연히 overriding을 하기 위해서는 super class의 해당 함수 앞에 open을 붙인 뒤 sub class에서는 override를 붙이면 된다.

다음과 같은 코드를 확인해보자

fun main(args: Array<String>) {
    val s = Student()
    s.studying()
}

open class KotlinOverriding {

    open fun studying() {
        println("여러분은 지금 상속과 추상화에 대한 개념을 보고 계십니다.")
    }

}

class Student: KotlinOverriding() {
    override fun studying() {
        println("이 포스팅을 kotlin언어를 다루고 있습니다.")
    }
}
  • 앞서 이야기한 것과 같이 코드를 작성했고 이 코드를 돌려보면, "이 포스팅을 kotlin언어를 다루고 있습니다."를 출력하고 있음을 알 수 있다.
  • 즉, 이미 super class에서 모든 구현이 끝났지만, 필요에 따라 해당 함수에 더 많은 기능을 첨부하고 싶을 때 사용할 수 있다.

😶‍🌫️ Kotlin에서의 추상화

  • 이번에는 super class에서 함수의 구체적인 구현이 없고 단지 모든 sub class는 studying라는 함수가 반드시 있어야 한다는 점만 명시하여 각 sub class가 super class에서 구현이 비어있는 함수의 내용을 필요에 따라 구현하도록 하려면, 추상화라는 개념을 사용한다.
  • 추상화: 선언부만 있고 기능은 구현되지 않은 추상함수 그리고 추상함수를 포함하는 추상 클래스라는 요소로 구성된다.
  • 추상 클래스에서 추상함수를 만들고 상속까지 받아 구현하는 방법에 대해 알아보자.
  • 여기서 중요한 것은 abstract를 붙인 추상 클래스는 일부 함수가 구현되지 않은 미완성 클래스이기 때문에 단독으로 인스턴스를 만들 수는 없다.
  • 그렇기 떄문에 sub class에서 상속받아 absract인 함수를 구현하여야 한다.

아래의 코드를 확인하자.

fun main(args: Array<String>) {
    val t = Teacher()
    t.note()
    t.studying()

}

abstract class AbstractDemo {
    abstract fun studying()
    fun note() {
        println("필기를 하고 있습니다.")
    }
}

class Teacher: AbstractDemo() {
    override fun studying() {
        println("선생님이 칭찬을 합니다.")
    }
}
  • 위 코드를 보면 구현하지 않은 studying이라는 함수가 내포된 AbstractDemo 클래스를 Teacher이라는 클래스에 상속하여 override 하는 것을 알 수 있다.
  • 그리고 난 뒤에 인스턴스를 만든 이후 함수를 사용하는 모습이다.

😶‍🌫️ Kotlin에서의 인터페이스

  • 추상화를 하는 또 다른 방법도 존재한다. 바로 인터페이스라는 기능이다.
  • 인터페이스는 추상함수만으로 이루어진 순수 추상화 기능을 말하는 것이다. 하지만 코틀린에서는 인터페이스 역시 추상함수, 일반함수, 속성을 갖을 수 있다.
  • 다만 추상함수는 생성자를 갖을 수 있는 반면 인터페이스는 생성자를 갖을 수는 없다. 또한 구현부가 있는 함수는 open 함수, 구현부가 없는 함수는 abstract 함수로 간주된다.
  • 따라서 별도의 키워드가 없어도 되며 모두 sub class에서 추가 구현 및 재정의 할 수 있다.
  • 또한 sub class는 한 번에 여러 개의 인터페이스를 상속 받을 수 있기 때문에 좀 더 유연한 대처가 가능하다.
  • 한 가지 주의할 점은 여러 개의 인터페이스나 클래스에서 같은 이름과 형태를 갖는 함수를 구현하고 있다면, sub class에서는 혼란이 발생하지 않도록 반드시 오버라이딩하여 재구현 해주어야 한다.

아래의 같이 여러 개의 인터페이스를 상속받는 코드를 작성해보자.

fun main(args: Array<String>) {
    val C = Conference()
    C.talking()
    C.listen()

}

interface Talker {
    fun talking()
}

interface Listener {
    fun listen() {
        println("이야기를 듣습니다.")
    }
}

class Conference: Talker, Listener {
    override fun talking() {
        println("발표자가 이야기 합니다.")
    }

    override fun listen() {
        println("발표자의 이야기를 듣습니다.")
    }
}

마지막 출력 값은 "발표자가 이야기 합니다.""발표자의 이야기를 듣습니다."이다.