kotlin OOB- inheretance
Kotlin의 모든 클래스는 Any를 상속 받음
class Example // Implicitly inherits from Any
Anyequals(), hashCode() 및 toString()는 Any에 정의되어 있어 모든 클래스에서 사용 가능
기본적으로 Kotlin 클래스는 final 이므로 상속할 수 없습니다. 클래스를 상속 가능하게 만들려면 다음 open키워드 로 클래스를 표시하십시오.
open class Base // Class is open for inheritance
명시적 상위 유형을 선언하려면 클래스 헤더에서 콜론 뒤에 유형을 배치하십시오.
open class Base(p: Int)
class Derived(p: Int) : Base(p)
파생(derived) 클래스에 기본 생성자가 있는 경우 매개 변수에 따라 해당 기본 생성자에서 기본 클래스를 초기화할 수 있으며 초기화해야 합니다.
파생 클래스에 기본 생성자가 없는 경우 각 보조 생성자는 super 키워드를 사용하여 기본 형식을 초기화 하거나 다른 생성자에 위임해야 합니다. 이 경우 다른 보조 생성자가 기본 유형의 다른 생성자를 호출할 수 있습니다.
class MyView : View {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}
Overriding methods
Kotlin에서는 override 생성자를 반드시 붙여야함
open class Shape {
open fun draw() { /*...*/ }
fun fill() { /*...*/ }
}
class Circle() : Shape() {
override fun draw() { /*...*/ }
}
Circle.draw()는 override에 필요합니다 . open와 같은 함수에 수정자(open)가 없으면 하위 클래스에서 동일한 서명을 사용하여 메서드를 선언하는 것이 허용되지 않습니다. open은 final class에서 안됨
재정의를 금지하려면 final 하십시오.
open class Rectangle() : Shape() {
final override fun draw() { /*...*/ }
}
Overriding 속성
overriding 메커니즘은 메서드에서와 동일한 방식으로 속성에서 작동합니다. 파생 클래스에서 다시 선언되는 슈퍼클래스에서 선언된 속성은 앞에 override가 있어야 하며 호환 가능한 유형이어야 합니다.
open class Shape {
open val vertexCount: Int = 0
}
class Rectangle : Shape() {
override val vertexCount = 4
}
val 속성을 var 속성으로 재정의할 수도 있지만 그 반대는 불가능합니다. 이는 val속성이 본질적으로 get메서드를 선언하고 이를 재정의 하면 파생 클래스에서 var추가로 메서드를 선언하기 때문에 허용됩니다.set override기본 생성자에서 속성 선언의 일부로 키워드를 사용할 수 있습니다 .
interface Shape {
val vertexCount: Int
}
class Rectangle(override val vertexCount: Int = 4) : Shape // Always has 4 vertices
class Polygon : Shape {
override var vertexCount: Int = 0 // Can be set to any number later
}
Derived class 초기화 순서
파생 클래스의 새 인스턴스를 생성하는 동안 기본 클래스 초기화가 첫 번째 단계로 수행됩니다(기본 클래스 생성자 선행). 즉, 파생 클래스의 초기화 전에 기본 클래스가 먼저 수행됨
open class Base(val name: String) {
init { println("Initializing a base class") }
open val size: Int =
name.length.also { println("Initializing size in the base class: $it") }
}
class Derived(
name: String,
val lastName: String,
) : Base(name.replaceFirstChar { it.uppercase() }.also { println("Argument for the base class: $it") }) {
init { println("Initializing a derived class") }
override val size: Int =
(super.size + lastName.length).also { println("Initializing size in the derived class: $it") }
}
이는 기본 클래스 생성자가 실행될 때 파생 클래스에서 선언되거나 재정의된 속성이 아직 초기화되지 않았음을 의미합니다. 기본 클래스 초기화 에서 이러한 속성을 사용하면(직접적으로 재정의된 다른 open멤버 구현을 통해 간접적으로) 잘못된 동작이나 런타임 오류가 발생할 수 있습니다. 따라서 기본 클래스를 디자인할 때 open생성자, 속성 이니셜라이저 또는 init블록에서 멤버를 사용하지 않아야 합니다.
슈퍼클래스 구현 호출
파생 클래스의 코드는 super 키워드를 사용하여 슈퍼클래스 함수 및 속성 접근자 구현을 호출할 수 있습니다 .
open class Rectangle {
open fun draw() { println("Drawing a rectangle") }
val borderColor: String get() = "black"
}
class FilledRectangle : Rectangle() {
override fun draw() {
super.draw()
println("Filling the rectangle")
}
val fillColor: String get() = super.borderColor
}
내부 클래스 내에서 외부 클래스의 수퍼클래스에 액세스하는 것은 super 를 사용. 외부 클래스 이름 super@Outer.
class FilledRectangle: Rectangle() {
override fun draw() {
val filler = Filler()
filler.drawAndFill()
}
inner class Filler {
fun fill() { println("Filling") }
fun drawAndFill() {
super@FilledRectangle.draw()
// Calls Rectangle's implementation of draw()
fill()
println("Drawn a filled rectangle with color
${super@FilledRectangle.borderColor}")
// Uses Rectangle's implementation of borderColor's get()
}
}
}
Overriding 규칙
Kotlin에서 구현 상속은 다음 규칙에 의해 규제됩니다. 클래스가 직속 슈퍼클래스에서 동일한 멤버의 여러 구현을 상속하는 경우 이 멤버를 재정의하고 자체 구현을 제공해야 합니다(아마도 상속된 것 중 하나 사용).
상속된 구현이 취해진 상위 유형을 표시하려면 super<Base>과 같이 꺾쇠 괄호 안에 상위 유형 이름으로 한정된 사용을 사용하십시오
open class Rectangle {
open fun draw() { /* ... */ }
}
interface Polygon {
fun draw() { /* ... */ } // interface members are 'open' by default
}
class Square() : Rectangle(), Polygon {
// The compiler requires draw() to be overridden:
override fun draw() {
super<Rectangle>.draw() // call to Rectangle.draw()
super<Polygon>.draw() // call to Polygon.draw()
}
}
Rectangle 및 Polygon에서 상속하는 것은 괜찮지 만 둘 다 draw()의 구현을 가지고 있으므로 Square class에서 draw()을 재정의 하고 모호성을 제거하기 위해 별도의 구현을 제공해야 합니다.
sample code
fun main(){
val audi = Car("bmw", "aos", 300.0)
val tesla = ElectricCar("S-model", "Tesla", 22, LocalDate.now().year -1, 250.0)
tesla.chargerType = "type 10"
audi.drive(100.0)
tesla.drive(200.0)
println("베터리 수명 : ${tesla.batteryLife}")
println("출고 년도 : ${tesla.year}")
println( tesla.drive()+", tesla max speed : "+tesla.maxSpeed)
tesla.brake()
audi.brake()
}
interface Drivable{
val maxSpeed : Double
fun drive() : String
fun brake() {
println("브레이크 밟아요!")
}
}
// Sub Class, Child Class or Derived Class of Vehicle
// Super class, parent Class, Base Class of ElectricCar
open class Car(val name : String, val brand: String, override val maxSpeed: Double) : Drivable {
open var range: Double = 0.0
fun extendRange(amount: Double) {
if(amount > 0)
range += amount
}
fun drive(distance: Double) { // overload
println("fun drove for $distance KM")
}
override fun drive(): String {
return "운전 중입니다."
}
}
// Sub Class, Child Class or Derived Class of Car
class ElectricCar(name: String, brand: String, var batteryLife: Int, var year: Int, maxSpeed: Double)
: Car(name, brand, maxSpeed){
var chargerType = "Type1"
override var range: Double = batteryLife * 2.0
override fun drive(): String {
return "Drove for $range"
}
override fun brake() {
// super.brake()
println("전기차에서 밟는 브레이크")
}
}