Kotlin/Basic

Kotlin은 Java와 무엇이 다른가? (9) - 상속

bongveloper 2024. 9. 19. 19:47

상속

Kotlin에서 상속은 문법이 같은 부분보다는 다른 부분이 좀 더 많다.

추상 클래스(Abstract Class)

  • 추상 클래스는 구현이 일부 없는 불안전한 클래스이다.
  • 추상 클래스로 공통된 특성 뼈대를 만들고 상속 받는 클래스에서 살을 붙이는 형식으로 많이 쓰인다.

추상 클래스 선언 방법

Kotlin에서 추상 클래스는 abstract 키워드로 선언한다.

abstract class Vehicle(val brand: String, open var model: String) {
    abstract fun start() 
    abstract fun stop()
}
  • 추상 메서드도 앞에 abstract를 붙여 사용 가능하다.
  • Java와 같은점은 추상클래스는 인스턴스화할 수 없다.

상속

Kotlin에서 상속 할 때에는 : 객체 를 가지고 상속한다.

class ElectricCar(val brand: String) : Vehicle(brand, ""){

    override fun start()  { 
        println("$brand  $model 전기차 출발") 
    }

    override fun stop()  { 
        println("$brand  $model 전기차 멈춤") 
    }

    override var model: String = "전기차"
}

중요한 점은

  • Type을 쓸 때도 :을 이용해서 사용한다.
    • 둘의 차이점은
    • Type을 쓸 때에는 Type: 띄어쓰기 없이 사용
    • 상속에 사용할 때에는 : 객체 한칸 띄어서 사용
  • 오버라이드를 하는 경우에는 override키워드를 사용해서 구현
  • 프로퍼티를 상속하는 경우에는 상위 프로퍼티에 open 키워드를 붙여주는 것이다.

인터페이스

Kotlin의 인터페이스는 Java와 유사한 점이 많다.

interface Vehicle {
    fun start() { 
        println("출발")
    } 
    fun stop() { 
        println("멈춤")
    }
    // 추상 메서드
    fun drive() 
}
  • Kotlin 인터페이스의 메서드는 기본적으로 추상 메서드이며, 로직이 있는 경우에만 default method가 된다.
  • 추상 method를 사용하고 싶다면 아무것도 있지 않은 method를 구현하면 된다.

인터페이스 구현 방법

Kotlin에서 인터페이스도 상속과 마찬가지로 : 키워드를 가지고 구현할 수 있다.

class ElectricCar : Vehicle{
    override fun start() {
        println("전기차 시작")
    }

    override fun stop() {
        super<Vehicle>.stop()
    }    
}
  • 상속과 똑같이 override 키워드를 사용해서 메서드를 구현한다.
  • 다른 점은 상위 인터페이스의 함수를 overide 할 때에는 super <객체>. 메서드로 구현이 가능하다.
  • Java와 마찬가지로 인터페이스를 인스턴스화할 수 없다.

Kotlin에서 조금 더 추가되는 점은 상위 인터페이스에 backing field가 없는 프로퍼티를 인터페이스에 만들 수 있다.

interface Vehicle {
    fun start() { 
        println("출발")
    } 
    fun stop() { 
        println("멈춤")
    }
    // 추상 메서드
    fun drive() 

    val brand: String
        get() = "Hyundai"
}
class ElectricCar : Vehicle{
    override fun start() {
        println("전기차 시작")
    }

    override fun stop() {
        super<Vehicle>.stop()
    }        

    override val brand: String
        get() = "KIA"
}

위와 같이
상위 인터페이스에 프로퍼티가 backing field가 사용되지 않았다면 구현체에서 override 해서 사용이 가능하다.

상속 시 주의점

open class Vehicle(open val brand: String = "Hyundai") {
    init {
        println("Vehicle 초기화: brand = $brand")
    }
} 

class Car(override brand: String, val model: String) : Vehicle(brand) {
    private val description: String
    init {
        println("Car 초기화 시작")
        println("model = $model")
        description = "$brand $model"
        println("description = $description")
    }
}
fun main() {
    val myCar = Car("Toyota", "Corolla")
}

해당 코드의 결과는 어떻게 될까?

Vehicle 초기화: brand = 
Car 초기화 시작
model = Corolla
description = Toyota Corolla

Vehicle 초기화 할 때 brand가 나오지 않게 될 것이다.
brand가 나오지 않는 이유는 상속을 할 때 생성자 호출 순서에 있다.

  • Car가 초기화되기 전에 Vehicle이 먼저 초기화가 된다.
  • Vehicle이 초기화될 때 하위 Car에 brand를 가져오게 되는데
  • 하지만 Car는 아직 초기화가 되지 않았으므로 brand에는 아무것도 없다.(Default Parameter를 선언했음에도)

이러한 문제를 막기 위해서
상위 클래스를 설계할 때 생성자 또는 초기화 블록에 사용되는 프로퍼티에는 open을 피해야 한다.라는 주의점이 있다.

요약

  • Kotlin에서 추상 클래스는 abstract 키워드를 사용해 구현한다.
  • 상속을 할 때에는 : 객체 키워드를 사용한다.
  • Kotlin에서 모든 클래스와 메서드는 기본적으로 final이며, 상속이나 오버라이드를 허용하려면 open 키워드를 사용해야 한다.
  • Kotlin 인터페이스의 메서드는 기본적으로 추상 메서드이며, 로직이 있는 경우에만 default method가 된다.
  • 인터페이스 구현체도 : 객체 키워드를 사용한다.
  • 상위 인터페이스에 프로퍼티가 backing field가 사용되지 않았다면 구현체에서 override 해서 사용이 가능하다.
  • 상위 클래스를 설계할 때 생성자 또는 초기화 블록에 사용되는 프로퍼티에는 open을 피해야 한다.

출처 : 자바 개발자를 위한 코틀린 입문(Java to Kotlin Starter Guide)

 

자바 개발자를 위한 코틀린 입문(Java to Kotlin Starter Guide) 강의 | 최태현 - 인프런

최태현 | 이 강의를 통해 Kotlin 언어의 특성과 배경, 문법과 동작 원리, 사용 용례, Java와 Kotlin을 함께 사용할 때에 주의할 점 등을 배울 수 있습니다., 요즘 대세인 코틀린을 공부하고 싶다면?⭐ J

www.inflearn.com