1. 상속
상속이란 기존 클래스를 재사용하면서 변경해 새로운 클래스를 만드는 메커니즘으로 기존 클래스의 프로퍼티와 동작을 재사용하고 확장할 수 있습니다.
open class Parent
class Child : Parent()
코틀린의 클래스는 기본적으로 final로 선언되기 때문에 상속이 불가능합니다. 클래스를 상속하기 위해서는 open 키워드를 통해 상속을 명시적으로 허용해줘야합니다.
- 부모 클래스에 open 키워드를 통해 상속을 명시적으로 허용해준다.
- 사용하는 자식 클래스에서 : 키워드를 사용한다.
open 키워드를 통해 해당 클래스가 상속을 고려해 설계되었다는 것을 명시적으로 알 수 있습니다.
1-1 생성자와 상속
상속하는 클래스는 상위 클래스의 생성자를 호출해야 합니다. 코틀린에서는 상위클래스의 주 생성자를 호출하기 위해 :suprer()를 사용합니다.
open class Parent(val name: String){
init {
println("부모 클래스 입니다~")
}
}
class Child(name: String, age: Int) : Parent(name) {
init {
println("자식 클래스입니다.")
}
}
fun main() {
val child = Child("자식", 10);
}
부모 클래스 입니다~
자식 클래스입니다.
1-2 상속과 확장함수
open class Parent(){
val name = "준준"
}
class Child : Parent()
fun Parent.info() = println("$name") //확장함수
fun main() {
Child().info() //확장 함수 호출
}
info는 Parent의 확장함수입니다. 이 때 Parent를 상속 받은 Child 클래스에서도 info()함수를 호출 할 수 있는데 상속은 Parent를 상속한 모든 존재가 항상 Parent임을 보장하기 때문입니다.
그 덕분에 Child 클래스는 Parent클래스의 함수와 프로퍼티를 사용할 수 있습니다. 이를 통해 코드를 단순화하고 재사용할 수 있다는 장점이 있습니다.
1-3 상속과 오버라이딩
open class Parent {
protected var count = 0
open fun call() = println("부모 클래스")
open fun sum() {
count += 10
}
}
class Child : Parent() {
override fun call() = println("자식 클래스")
override fun sum() {
count += 100
super.sum()
}
}
자식 클래스는 부모 클래스의 private 멤버에 접근할 수 없습니다.
protected는 외부에는 닫혀 있고, 자식 클래스에서 접근, 오버라이드가 가능합니다. 위의 예제에서는 count가 protected로 되어 있기 때문에 자식클래스에서 접근을 허용하는 동시에 외부로부터 해당 프로퍼티를 숨길 수 있습니다.
자식 클래스에서 부모 클래스와 똑같은 시그니처를 갖는 함수를 정의하면 부모클래스에 정의한 함수가 수행하던 동작을 새로 정의한 함수의 동작으로 대체되는데 이를 오버라이딩이라고 합니다.
함수를 오버라이드 할 때에는 open이 아닌 클래스를 상속할 수 없는 것처럼 부모 클래스의 함수가 open으로 지정되어 있지 않으면 자식 클래스에서 오버라이드가 불가능합니다.
1-4 상속과 다형성
fun talk(ape: Parent) {
ape.call()
}
fun main() {
talk(Parent())
talk(Child())
}
또한 자식 클래스 타입의 객체를 부모 클래스의 객체처럼 취급할 수 있습니다.
1. talk() 함수는 파라미터로 Parent 타입을 받는다.
이 때 부모 클래스를 상속받은 자식클래스의 객체도 파라미터로 전달이 가능하다.
2. talk() 에서 호출되는 call() 메서드는 객체의 실제 타입에 따라 동작이 달라진다.
- talk(Parent()) 호출 시 Parent 클래스의 call() 메서드 실행
- talk(Child()) 호출 시, Child 클래스의 call() 메서드 실행
이러한 동작은 객체의 실체 타입에 따라 적절한 메서드가 실행되는 다형성의 특징으로 이를 통해 부모와 자식의 구체적인 구현을 몰라도 동일한 방식으로 호출이 가능하며 각각 객체에 맞는 동작을 수행할 수 있습니다.
다형성의 장점
takl() 함수는 Parent뿐만 아니라 상속받은 모든 자식 클래스와 함께 동작이 가능 새로운 자식 클래스를 추가해도 talk()를 수정할 필요가 없다. => 유연한 설계와, 확장성 증가
다형성이란? 하나의 인터페이스로 다양한 동작을 수행 -> 객체의 실제 타입에 따라 다른 구현이 실행되는 특성
2. 기반 클래스 초기화
클래스가 다른 클래스를 상속할 때 코틀린은 두 클래스가 모두 제대로 초기화 되도록 보장합니다.
코틀린은 아래와 같은 생성자가 호출되도록 보장함으로써 올바른 객체를 생성합니다.
- 멤버 객체들의 생성자
- 자식 클래스에 추가된 객체의 생성자
- 부모 클래스의 생성자
open class Parent(val name: String) {
init {
println("부모 이름 초기화: $name")
}
}
class Child(name: String, val age: Int) : Parent(name) {
init {
println("자식 나이 초기화: $age")
}
}
fun main() {
val child = Child("준준", 20)
}
부모 이름 초기화: 준준
자식 나이 초기화: 20
초기화 과정
1. Parent와 Child 클래스에서 멤버 변수의 초기화 진행
2. 자식 클래스의 생성자 호출
자식 클래스의 주 생성자가 호출되며 이 때 부모 클래스의 생성자 호출을 명시적으로 정의해야합니다.(: Parent(name))
3. 부모 클래스의 생성자 호출 Parent 클래스의 생성자가 호출되어 초기화 작업 수행
Parent 클래스의 생성자가 호출되어 초기화 작업 수행합니다.
4. 자식 클래스의 초기화 완료
Child 클래스의 초기화 블록이 실행되어 나머지 작업이 완료됩니다.
왜 이런 과정이 진행되는가..?
-> 올바른 객체 생성을 보장함 : 부모 클래스와 자식 클래스가 정상적으로 초기화
-> 초기화 순서를 명확히 함으로써 예기치 않은 동작을 방지
3. 총정리
- 상속: 기존 클래스 재사용 및 확장을 통해 코드 재사용성을 높인다.
- 생성자 호출: 자식 클래스에서 부모 생성자를 명시적으로 호출해야 한다.
- 오버라이딩: 부모 클래스 메서드를 재정의하려면 open 키워드가 필요하며, super로 부모 동작 호출 가능하다.
- 다형성: 부모 타입 참조로 자식 객체를 처리하여 유연하고 확장 가능한 설계 제공한다.
- 초기화 순서: 멤버 변수 초기화 → 자식 생성자 호출 → 부모 생성자 호출 → 자식 초기화 완료.
- 이를 통해 객체 생성의 일관성과 안정성을 보장.

'Kotlin 정리' 카테고리의 다른 글
[Kotlin 공부 11일차] 추상 클래스, 업캐스트 (0) | 2025.01.06 |
---|---|
[Kotlin 공부 9일차] 인터페이스 (0) | 2024.12.17 |
[Kotlin 공부 8일차] 널이 될 수 있는 타입 (0) | 2024.12.12 |
[Kotlin 공부 7일차] 데이터 클래스, 구조 분해 선언 (0) | 2024.12.10 |
[Kotlin 공부 6일차] 오버로딩, when (0) | 2024.12.06 |