본문 바로가기

Study/Kotlin Study

[Kotlin] 하위 클래스 선언

+ 항공대학교 김철기 교수님의 객체 지향 프로그래밍 과목 내용를 정리한 글입니다.

하위 클래스 선언

상위 클래스 = 부모 클래스 = Super Class

하위 클래스 = 자식 클래스 = Sub Class

 

ex) Activiy - MainActivity , Vehicle - FlyingVehicle - Aircraft

 

+ 하나의 클래스는 하나의 상위 클래스를 가질 수 있다.

open 클래스

open으로 선언된 클래스는 상속이 가능하다.

- open이 아닌 클래스는 상속이 불가능하다.

 

: 뒤의 상위 클래스 ()는 상위 클래스 생성자 호출을 의미한다.

- 필수적으로 상위 클래스의 생성자를 사전에 호출해야한다.

 

하위 클래스 객체는 모두 상위 클래스에도 소속된다.

멤버의 상속

-하위 클래스는 상위 클래스의 멤버를 모두 상속한다.

// open을 선언한 클래스만 상속 가능
open class Vehicle {
    var currentSpeed = 0

    fun start() {
        println("I'm moving")
    }

    fun stop() {
        println("Stopped")
    }
}

open class FlyingVehicle : Vehicle() {
    fun takeOff() {
        println("Taking off")
    }

    fun landing() {
        println("Landed")
    }
}

class Aircraft(val seats: Int) : FlyingVehicle()

fun main() {
    val aircraft = Aircraft(100)
    val vehicle: Vehicle = aircraft // 상위 타입 변수에 하위 타입을 대입할 수 있다.

    vehicle.start()
    vehicle.stop()
    vehicle.takeoff() // 에러 발생

    aircraft.start()
    aircraft.takeOff()
    aircraft.landing()
    aircraft.stop()
    println(aircraft.seats)
}

멤버 상속 구현 (다형성, Polymorphism)

상위 클래스의 open 멤버를 override하여 구현을 수정할 수 있다.

open class Vehicle {
    var currentSpeed = 0

    open fun start() {
        println("I'm moving")
    }

    fun stop() {
        println("Stopped")
    }
}

class Car : Vehicle() {
    override fun start() {
        println("I'm riding")
    }
}

class Boat : Vehicle() {
    override fun start() {
        println("I'm sailing")
    }
}

fun main() {
    val vehicle1: Vehicle = Car()
    val vehicle2: Vehicle = Boat()

    // 변수의 타입보다 실제 객체의 타입이 더 중요하다!
    vehicle1.start() // I'm riding
    vehicle2.start() // I'm sailing
}

확장과 상속(override)의 차이

상속: 인스턴스의 실제 타입으로 호출 대상이 결정된다.

 

확장: 변수의 타입에 따라 정적으로 호출 대상이 결정된다.

open class Vehicle {
    var currentSpeed = 0

    open fun start() {
        println("I'm moving")
    }
}

fun Vehicle.stop() {
    println("Stopped")
}

class Car : Vehicle() {
    override fun start() {
        println("I'm riding")
    }
}

fun Car.stop() {
    println("Stopped riding")
}

class Boat : Vehicle() {
    override fun start() {
        println("I'm sailing")
    }
}

fun main() {
    val car: Car = Car()
    val vehicle1: Vehicle = Car()
    val vehicle2: Vehicle = Boat()

    // 변수의 타입보다 실제 객체의 타입이 더 중요하다!
    vehicle1.start() // I'm riding
    vehicle2.start() // I'm sailing

    // 변수의 타입에 맞는 함수를 호출한다. (확장)
    vehicle1.stop() // Stopped (Vehicle 타입 변수 -> Vehicle 객체 내 함수 호출)
    vehicle2.stop() // Stopped
    car.start()
    car.stop() // Stopped riding (Car 타입 변수 -> Car 객체 내 함수 호출)
}

상속과 멤버 시그니처

상속하는 멤버의 시그니처와 상위 클래스 멤버 시그니처는 동일해야한다.

시그니처: 파라미터들의 순서, 타입과 반환 값 타입

open class Vehicle {
    var currentSpeed = 0

    open fun start(speed: Int) {
        println("I'm moving at $speed")
    }

    fun stop() {
        println("Stopped")
    }
}

class Car : Vehicle() {
    override fun start(speed: Int) { // Vehicle 클래스의 start 함수의 시그니처와 동일
        println("I'm riding at $speed")
    }
}


fun main() {
    val car: Car = Car()
    val vehicle: Vehicle = car

    vehicle.start(100)
    vehicle.stop()

    car.start(100)
    car.stop()
}

final을 통한 추가 상속의 제한

override한 멤버는 open을 선언하지 않아도 추가 상속을 할 수 있다.

open class Vehicle {
    var currentSpeed = 0

    open fun start() {
        println("I'm moving")
    }

    fun stop() {
        println("Stopped")
    }
}

open class Car : Vehicle() {
    override fun start() { // open을 선언하지 않아도 상속 가능
        println("I'm riding")
    }
}

class Bus : Car() {
    override fun start() {
        println("I'm riding a bus")
    }
}

 

override하는 멤버를 final로 선언하면 추가 상속을 할 수 없다.

open class Vehicle {
    var currentSpeed = 0

    open fun start() {
        println("I'm moving")
    }

    fun stop() {
        println("Stopped")
    }
}

open class Car : Vehicle() {
    final override fun start() { // final 선언
        println("I'm riding")
    }
}

class Bus : Car() {
    override fun start() { // Car의 start 함수 추가 상속 불가 -> 에러
        println("I'm riding a bus")
    }
}

프로퍼티의 상속

주생성자 파라미터를 통해 하위 클래스 본문에 구현을 넣어 상속 가능하다.

open class Entity {
    open val name: String
        get() = ""
}

class Person(override val name: String) : Entity()

fun main() {
    val person: Person = Person("Sangsin")
    println(person.name)
}

 

불변 프로퍼티를 가변 프로퍼티로 상속 가능하다. (반대는 불가능)

open class Entity {
    open val name: String
        get() = ""
}

class Person() : Entity() {
    override var name = ""
}

fun main() {
    val person: Person = Person()
    person.name = "Sangsin"

    println(person.name)
}

protected 접근 제어자

protected를 통해 상속 체계 밖에서의 멤버 접근을 막을 수 있다.

open class Vehicle {
    protected open fun onStart() {}
    fun start() {
        println("Starting up ...")
        onStart()
    }
}

class Car : Vehicle() {
    override fun onStart() {
        println("It's a car")
    }
}

fun main() {
    val car = Car()
    car.start()
    car.onStart() // main 함수는 클래스 밖에 있으므로 접근 불가
}

'Study > Kotlin Study' 카테고리의 다른 글

[Kotlin] 타입 검사와 캐스팅  (2) 2023.10.23
[Kotlin] 하위 클래스 초기화  (0) 2023.10.23
[Kotlin] Fragment 실습  (2) 2023.10.14
[Kotlin] 영역 함수  (0) 2023.10.09
[Kotlin] 확장  (0) 2023.10.09