2020. 3. 3. 22:40ㆍ소프트웨어 공학/코딩 공부
본 내용은 kotlinlang.org 공식 문헌과 기타 자료들을 바탕으로 필자가 학습한 내용을 정리한 것입니다.
필자의 허락 없이 글을 상업적 목적으로 수정, 재배포할 수 없습니다. 내용의 오류 지적은 덧글로 받습니다.
최상위 클래스 / Any
모든 클래스는 공통적으로 Any
를 상속하는데, 부모 클래스를 정의하지 않아도 마찬가지다.Any
클래스가 갖는 메서드는 두개지요! 3개입니다.
- equals() - 두 객체가 서로 일치하는 값인지 조사하는 함수
- hashCode() - 두 객체의 메모리 주소가 일치하는지 조사하는 함수
- toString() - 객체 정보를 문자열로 변환하는 함수
상속 / Inheritance
// 부모 클래스
open class Base {
constructor(x: Int)
constructor(x: Int, y: Int)
}
// 초기 생성자 있음
class ClassA(x: Int) : Base(x)
// 초기 생성자 없음
class ClassB : Base {
constructor(x: Int) : super(x)
constructor(x: Int, y: Int) : super(x, y)
}
위 예제에서 ClassA, ClassB는 Base를 상속하는 자식 클래스이다. 차이점은 ClassA만 초기 생성자를 갖는다. 1
초기 생성자가 있다면, 반드시 클래스 헤더에서 부모 클래스를 초기화해야 한다. 2
초기 생성자가 없다면, 생성자에서 super 키워드를 참조하여 부모를 초기화해야 한다.
생성자 뒤에 콜론(:)과 함께 클래스명을 적으면 해당 클래스를 상속하게 되는데, 다중 상속은 불가능하다. 3
이때 다시 상기할 부분은, 인스턴스를 생성하거나 클래스를 상속할 때 해당 클래스를 초기화하는 것은 소괄호(bracket) 표시이다. e.g. val instance = Class( )
재정의 / Override
부모 클래스의 함수, 프로퍼티가 자식에 의해 재정의 되는 것을 허용하려면 open 키워드를 통해 이를 명시해야 한다. 4 만약 이를 준수하지 않고 override 한다면 컴파일 오류가 발생한다. 5
자식 클래스가 부모의 함수나 프로퍼티를 재정의하려면 override 키워드를 사용하자.
open class Human {
open var name = "Human"
open var age = 0
open fun introduce() {
println("Wait, who am I?")
}
}
// 초기 생성자에서 name 프로퍼티를 재정의함.
class Steve(override var name: String = "Steve") : Human() {
override val age = 10 // 재정의 오류! var -> val 로 덮어쓸 수 없다.
override fun introduce() {
println("My name is $name. I am $age years old.")
}
}
fun main() {
Human().introduce() // 출력: Wait, who am I?
Steve().introduce() // 출력: My name is Steve. I am 10 years old.
}
override 키워드를 붙이면 해당 클래스 멤버는 open 상태가 된다. 하위 클래스가 이것을 재정의하지 못하게 하려면 final 키워드를 사용하자.
프로퍼티를 override 할 때 2가지 규칙이 있다.
- 초기 생성자(클래스 헤더)에서 프로퍼티를 재정의할 수 있다.
- val을 var로 재정의할 수 있지만 그 반대는 불가능하다. var 프로퍼티는 getter와 setter가 모두 정의되었기 때문에 getter만 정의된 val로 되돌릴 수 없기 때문이다.
주의할 점 / Override caution
상속 구조를 가진 클래스를 만들면 초기화는 최상위 클래스부터 시작한다는 사실을 간과하면 안 된다.
만약 상위 클래스가 초기화 도중 open 멤버(함수, 프로퍼티)를 사용하게 되면, 아직 초기화되지 않은 하위 클래스가 재정의하는 값을 건드리는 꼴이 되므로 예기치 못한 문제가 발생할 수 있다.
아래의 상황에서 open 멤버를 사용하는 것은 지양하자:
- 생성자 내부
- 프로퍼티 초기화(값 대입)
- 초기화 블록
상위 멤버 호출
위 그림처럼 다중 상속에서 발생하는 문제점이 있다. 자식 클래스가 중복되는 이름의 클래스 멤버를 상속하면, 어떤 것을 선택할지 답이 모호하다는 것이다. 이 경우 해당 멤버를 재정의할 필요가 있는데, 중복되는 멤버 중 한쪽을 선택할 때 super 표기법을 이용할 수 있다: super<Base>
fun main(args: Array<String>) {
GoodBoy().sound() // 출력: woof
}
interface Pet {
open fun sound() {
println("no sound")
}
}
open class Dog {
open fun sound() {
println("woof")
}
}
class GoodBoy : Dog(), Pet {
override fun sound() {
super<Dog>.sound() // Dog의 sound()를 호출
}
}
내부 클래스에서 super 호출
Base를 상속하는 Main 클래스 안에 내부 클래스가 또 정의되어 있다고 하자. 내부 클래스에서 Base를 접근하려면 어떻게 해야 될까? 이렇게 하면 된다: super@Main
open class Base {
open var i = 1
}
class Main : Base() {
inner class MainInner { // 내부 클래스
fun test() {
super@Main.i = 10 // Base에 접근, 프로퍼티 i 수정
}
}
}
'소프트웨어 공학 > 코딩 공부' 카테고리의 다른 글
Java - JSR223 스크립트 API (0) | 2020.03.06 |
---|---|
3-3. Kotlin 클래스 추상화 (0) | 2020.03.04 |
3-1. Kotlin 클래스 생성자 (0) | 2020.03.03 |
3. Kotlin 클래스 (0) | 2020.03.03 |
2. Kotlin 자료형 (1) | 2020.03.02 |