Kotlin의 상속(inheritance)
Kotlin에서 상속은 객체 지향 프로그래밍의 핵심 개념 중 하나입니다.
이 가이드에서는 Kotlin의 상속 메커니즘을 상세히 살펴보고, 실제 사용 사례와 함께 이해를 돕겠습니다.
기본 개념 (Basic Concepts)
Kotlin의 모든 클래스는 공통 상위 클래스인 Any
를 가집니다. 명시적으로 상위 타입을 선언하지 않은 클래스는 자동으로 Any
를 상속받습니다:
class Example // 암시적으로 Any를 상속
Any
는 equals()
, hashCode()
, toString()
세 가지 메서드를 가지고 있어, 이 메서드들은 모든 Kotlin 클래스에서 정의됩니다.
기본적으로 Kotlin 클래스는 final이며 상속될 수 없습니다. 클래스를 상속 가능하게 만들려면 open
키워드를 사용해야 합니다:
open class Base // 상속을 위해 열려있는 클래스
명시적인 상위 타입을 선언하려면 클래스 헤더에 콜론 뒤에 타입을 배치합니다:
open class Base(p: Int)
class Derived(p: Int) : Base(p)
메서드 오버라이딩 (Overriding Methods)
Kotlin은 오버라이드 가능한 멤버와 오버라이드에 대해 명시적 변경자를 요구합니다:
open class Shape {
open fun draw() { /*...*/ }
fun fill() { /*...*/ }
}
class Circle() : Shape() {
override fun draw() { /*...*/ }
}
Circle.draw()
에는 override
변경자가 필요합니다. 이를 생략하면 컴파일러가 오류를 발생시킵니다. Shape.fill()
과 같이 open
변경자가 없는 함수는 하위 클래스에서 같은 시그니처의 메서드를 선언할 수 없습니다.
override
로 표시된 멤버는 그 자체로 open이므로 하위 클래스에서 다시 오버라이드할 수 있습니다. 재오버라이딩을 금지하려면 final
을 사용합니다:
open class Rectangle() : Shape() {
final override fun draw() { /*...*/ }
}
프로퍼티 오버라이딩 (Overriding Properties)
프로퍼티 오버라이딩은 메서드 오버라이딩과 동일한 메커니즘으로 작동합니다:
open class Shape {
open val vertexCount: Int = 0
}
class Rectangle : Shape() {
override val vertexCount = 4
}
val
프로퍼티를 var
프로퍼티로 오버라이드할 수 있지만, 그 반대는 불가능합니다.
주 생성자에서도 override
키워드를 사용할 수 있습니다:
interface Shape {
val vertexCount: Int
}
class Rectangle(override val vertexCount: Int = 4) : Shape // 항상 4개의 꼭짓점을 가짐
class Polygon : Shape {
override var vertexCount: Int = 0 // 나중에 다른 숫자로 설정 가능
}
파생 클래스 초기화 순서 (Derived Class Initialization Order)
파생 클래스의 새 인스턴스를 생성할 때, 기본 클래스 초기화가 첫 번째 단계로 수행됩니다:
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") }
}
이는 기본 클래스 생성자가 실행될 때 파생 클래스에서 선언되거나 오버라이드된 프로퍼티가 아직 초기화되지 않았음을 의미합니다.
상위 클래스 구현 호출 (Calling the Superclass Implementation)
파생 클래스의 코드에서 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@Outer
와 같이 사용합니다:
class FilledRectangle: Rectangle() {
override fun draw() {
val filler = Filler()
filler.drawAndFill()
}
inner class Filler {
fun fill() { println("Filling") }
fun drawAndFill() {
super@FilledRectangle.draw() // Rectangle의 draw() 구현을 호출
fill()
println("Drawn a filled rectangle with color ${super@FilledRectangle.borderColor}") // Rectangle의 borderColor의 get() 구현을 사용
}
}
}
오버라이딩 규칙 (Overriding Rules)
Kotlin에서 구현 상속은 다음 규칙에 의해 규제됩니다: 클래스가 직접적인 상위 클래스로부터 동일한 멤버의 여러 구현을 상속받는 경우, 해당 멤버를 오버라이드하고 자체 구현을 제공해야 합니다.
상속된 구현이 취해진 상위 타입을 표시하려면 꺾쇠 괄호 안에 상위 타입 이름으로 한정된 super
를 사용합니다:
open class Rectangle {
open fun draw() { /* ... */ }
}
interface Polygon {
fun draw() { /* ... */ } // 인터페이스 멤버는 기본적으로 'open'
}
class Square() : Rectangle(), Polygon {
// 컴파일러는 draw()를 오버라이드할 것을 요구합니다:
override fun draw() {
super<Rectangle>.draw() // Rectangle.draw() 호출
super<Polygon>.draw() // Polygon.draw() 호출
}
}
Kotlin의 상속 메커니즘은 강력하면서도 안전합니다. open
과 override
키워드를 통해 명시적인 상속 구조를 만들 수 있으며, 이는 코드의 가독성과 유지보수성을 높여줍니다. 또한 다중 상속의 모호성 문제를 해결하기 위한 명확한 규칙을 제공하여 안정적인 코드 작성을 돕습니다.
Kotlin의 상속을 효과적으로 활용하면 코드 재사용성을 높이고 객체 지향 설계의 이점을 최대한 활용할 수 있습니다. 하지만 과도한 상속은 코드를 복잡하게 만들 수 있으므로, 적절한 사용이 중요합니다.
이 블로그의 다른글 :
2024.08.10 - [개발&프로그래밍] - [Kotlin] 조건문과 반복문
[Kotlin] 조건문과 반복문
Kotlin의 조건문과 반복문Kotlin은 다양한 조건문과 반복문을 제공하여 프로그래머가 효율적이고 읽기 쉬운 코드를 작성할 수 있게 합니다. 이 가이드에서는 Kotlin의 주요 제어 흐름 구조를 살펴보
observerlife.tistory.com
2024.08.10 - [개발&프로그래밍] - [kotlin] 타입 체크와 캐스팅
[kotlin] 타입 체크와 캐스팅
Kotlin의 타입 체크와 캐스팅Kotlin에서 타입 체크와 캐스팅은 객체 지향 프로그래밍의 핵심 기능입니다. 이 가이드에서는 Kotlin의 타입 체크와 캐스팅 기능을 모든 코드 예제와 함께 상세히 살펴보
observerlife.tistory.com
2024.08.10 - [개발&프로그래밍] - [kotlin] 코딩 컨벤션 - lambdas, Trailing commas
[kotlin] 코딩 컨벤션 - lambdas, Trailing commas
Kotlin 코딩 컨벤션 가이드Kotlin을 사용하여 더 깔끔하고 일관된 코드를 작성하기 위한 고급 코딩 컨벤션을 소개합니다.아래글은 공식 홈페이지를 참고하였습니다.https://kotlinlang.org/docs/coding-convent
observerlife.tistory.com
2024.08.09 - [개발&프로그래밍] - [kotlin] 코딩 컨벤션 (coding conventions)
[kotlin] 코딩 컨벤션 (coding conventions)
Kotlin 코딩 컨벤션 가이드 Kotlin 프로그래밍 언어를 사용할 때 따라야 할 코딩 컨벤션에 대한 종합적인 가이드입니다.이 가이드라인을 따르면 코드의 일관성과 가독성을 높일 수 있습니다. 이
observerlife.tistory.com
2024.08.09 - [개발&프로그래밍] - [kotlin] 자주 사용되는 관용구(idiom) 모음
[kotlin] 자주 사용되는 관용구(idiom) 모음
이 글은 Kotlin 프로그래밍 언어에서 자주 사용되는 관용구(idiom)들의 모음입니다.각 관용구에 대한 설명과 예제 코드를 함께 설명합니다.공식 홈페이지 kotlinlang.org의 내용을 참고합
observerlife.tistory.com
2024.08.07 - [개발&프로그래밍] - [kotlin] 입문시 알아야 할 8가지 핵심 문법
[kotlin] 입문시 알아야 할 8가지 핵심 문법
Kotlin의 주요 문법 특징들을 간략히 정리한 것입니다.각 항목은 Kotlin의 간결하고 표현력 있는 코드 작성을 지원하는 특징들을 보여줍니다. 아래 문서는 kotlinlang.org 공식 홈페이지를 참조합니다.
observerlife.tistory.com
2024.08.07 - [개발&프로그래밍] - [kotlin] 입문시 알아야 할 7가지 핵심 문법
[kotlin] 입문시 알아야 할 7가지 핵심 문법
Kotlin의 기본적인 문법 요소들을 간략하게 설명하고 있습니다.각 항목은 Kotlin 프로그래밍의 기초를 이해하는 데 중요한 개념들을 포함하고 있습니다. 아래 문서는 kotlinlang.org 공식 홈페이지를
observerlife.tistory.com
Kotlin, 상속, 오버라이딩, 객체지향프로그래밍, 다형성, 초기화순서, 상위클래스, 하위클래스, 인터페이스, 추상클래스, 안드로이드개발, 서버개발, 코틀린문법, 클래스설계, 코드재사용
'개발&프로그래밍' 카테고리의 다른 글
[kotlin] 인터페이스(interface) (0) | 2024.08.13 |
---|---|
[kotlin] 프로퍼티(Properties) (0) | 2024.08.13 |
[kotlin] 클래스 (0) | 2024.08.12 |
[kotlin] Exceptions (0) | 2024.08.12 |
[kotlin] Return 과 점프 표현식 (0) | 2024.08.12 |
댓글