본문 바로가기

코틀린

[Kotlin] 상속

 코틀린의 open, final 키워드는 클래스 상속 및 메서드/프로퍼티 오버라이딩을 결정하는 데 사용된다.

  • open : 상속 / 오버라이딩 가능
  • final : 상속 / 오버라이딩 불가

 클래스와 프로퍼티에 어떤 키워드도 설정하지 않는 경우 기본 값은 public final 로, 외부에 개방되지만 상속 및 오버라이딩은 불가능한 상태가 된다. 보통 프로그래밍 언어들은 기본적으로 상속을 허용하는데, 코틀린은 기본값으로 상속을 허용하지 않는다는 점이 특징적이다. 

Any class

 코틀린의 모든 클래스는 Any 클래스를 조상으로 가진다. C#이나 Java의 Object 클래스와 매우 유사하다.

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/

 

Any - Kotlin Programming Language

 

kotlinlang.org

클래스 상속

 open 키워드를 설정하면 다른 클래스에서 상속할 수 있게 된다. 이때 해당 클래스를 상속한 하위 클래스들의 기본 상속 옵션은 open이 되므로, 이상 상속을 막고 싶은 경우 final을 명시하여 상속을 막아야 한다.

abstract class Shape {} // 추상 클래스 선언

open class Rectangle : Shape() {} // Shape 클래스 상속
final class Square : Rectangle() {} // Rectangle 클래스를 상속하되, 더 이상 상속 막음

추상 클래스

 추상 클래스는 다른 언어들과 마찬가지로 abstract 키워드를 통해 지정한다. 추상 클래스의 함수와 프로퍼티 모두에 abstract를 붙여 실제 구현을 유도할 수 있다. 추상 클래스 안에 있더라도 abstract 키워드로 지정되지 않았다면 일반적인 클래스와 동일하게 동작한다.

abstract class Inanimate() {
    abstract val a : String
    abstract fun doSomething1()
    fun doSomething2() {
        println("do something!") // 일반적인 메서드도 가능
    }
}

// Inanimate 을 상속, 프로퍼티 a와 메서드 doSomething1을 오버라이딩
class Maan : Inanimate() {
    override val a: String
        get() {
            TODO()
        }

    override fun doSomething1() {
        doSomething2() 
    }
}

sealed 클래스/인터페이스

 상속받는 자식 클래스를 제한하기 위한 목적의 지시어다. 일반적으로 컴파일러는 특정 클래스를 상속한 클래스의 종류가 얼마나 되는지 알 방법이 없다. 모듈 바깥, 패키지 바깥에서도 해당 클래스를 상속할 수 있기 때문이다. 단순히 상속을 막는 것은 final로 처리가 되지만, 상속하는 클래스를 제한하는 방법은 final 수준에서 정의할 수 없다. 

 sealed 클래스/인터페이스는 자신을 상속하는 자식 클래스가 있되, 클래스의 존재 범위를 현재 모듈로 제한하고 싶은 경우에 사용한다. sealed로 선언된 클래스 자체는 추상 클래스의 성질을 가지며, protected 또는 private 생성자만 가지므로 직접 해당 클래스 자체를 만들 수는 없다.

sealed class Color(val r: UByte, val g: UByte, b: UByte) 

class Red : Color(255u, 0u, 0u)
class Green : Color(0u, 255u, 0u)
class Blue : Color(0u, 0u, 255U)

 사용할 수 있는 색이 미리 존재하는 시스템에서, 사용자가 Color 클래스를 상속하여 새로운 색을 만드는 것을 방지하고 싶은 경우를 생각해보자. sealed을 적용하면 동일 모듈 내에서는 Color을 상속하는 Red, Green, Blue 클래스를 선언할 수 있으나 모듈 외부에서는 Class을 상속하여 다른 색에 대한 클래스를 구현할 수 없다.

 사실 위 예시의 경우 색에 대한 인스턴스가 하나씩만 존재해야 한다면 enum class로 선언하는 것이 더 적합할 것이다. sealed 클래스는 자식 클래스의 인스턴스를 여러개 만들 수 있기 때문이다.

when을 통한 패턴 매칭

 sealed 클래스는 when expression과 함께 사용하면 효과적이다. 일반 클래스는 컴파일 타임에 자식 클래스의 종류나 개수를 식별할 수 없다. 이 경우 유저는 색이 Red, Green, Blue 만 존재하는 것을 아는 상황에서도 else를 추가해야 한다.

 반면 sealed 클래스는 컴파일 타임에 모듈 범위로 자식 클래스가 고정된다. 덕분에 전체 색에 대한 조건을 포함하지 않으면 에러 메시지가 발생하며, 전체를 포함하는 경우 else문을 생략할 수 있다.


오버라이딩

메서드 및 프로퍼티에 대해서 open 및 final 은 상속 대신 오버라이딩의 의미를 가진다. 프로퍼티 오버라이딩의 전제조건은 클래스가 open 으로 지정되어 있는 것이다. 당연한게, 상속하지 못하는 내용을 오버라이딩 할 수는 없기 때문이다.

 오버라이딩한 메서드 및 프로퍼티는 기본적으로 open 으로 지정한다. 만약 오버라이딩을 막고 싶다면 final을 명시한다.

메서드 오버라이딩

오버라이딩한 메서드는 open으로 지정된다.

 메서드를 오버라이딩할 때는 override 키워드를 명시한다. 오버라이딩 한 메서드는 open으로 지정된다.

 상속을 통해 만든 클래스를 open 으로 지정하여 상속을 허용하는 경우, 오버라이딩 한 메서드는 자동적으로 오버라이딩을 허용하는 셈이 된다. 따라서 이상의 오버라이딩을 막고 싶다면 명시적으로 final 키워드를 지정해야 한다.

프로퍼티 오버라이딩

 코틀린의 프로퍼티는 get 및 set 함수를 가진다. 프로퍼티를 abstract로 지정한다는 의미는 추상 클래스를 상속하는 클래스에게 해당 함수들에게 구현을 넘기는 것과 같다. 이때 val -> var은 가능하지만, 반대는 불가능하다.

interface Runable {
    var a : String // 프로퍼티 지정 자체는 가능한데, 초기화 X
    // 생성자 X
    fun run() {
        println("I Can run!")
    }
}

class Man : Runable {
    override var a: String
        get() = TODO("Not yet implemented")
        set(value) {}

    final override fun run() {
        println("Run like human")
    }
}

 

'코틀린' 카테고리의 다른 글

[코틀린] 자바&코틀린 라이브러리 검색  (0) 2022.11.05
[Kotlin] 생성자  (0) 2022.09.06
[Kotlin] control flow  (0) 2022.09.06
[Kotlin] 기본 타입  (0) 2022.09.03
[Kotlin] 기본 타입과 내부 처리  (0) 2022.09.01