kotlin의 기본 문법 정리(013)
1. open, final, abstract 변경자
취약한 기반 클래스(fragile base class)
하위 클래스가 기반 클래스에 대해 가졌던 가정이 기반 클래스를 변경함으로써 깨져버린 경우에 생긴다.
어떤 클래스가 자신을 상속하는 방법에 대해 정확한 규칙(어떤 메소드를 어떻게 오버라이드해야 하는지 등)을 제공하지 않는다면 그 클래스의 클라이언트는 기반 클래스를 작성한 사람의 의도와 다른 방식으로 메소드를 오버라이드할 위험이 있다. 모든 하위 클래스를 분석하는 것은 불가능하므로 기반 클래스를 변경하는 경우 하위 클래스의 동작이 예기치 않게 바뀔 수도 있다는면에서 기반 클래스는 ‘취약’하다.
// 열린 메소드를 포함하는 열린 클래스 정의하기
// 이 클래스는 열려있다. 다른 클래스가 이 클래스를 상속할 수 있다.
open class RichButton: Clickable {
// 이 함수는 final이다. 하위 클래스가 이 메소드를 오버라이드 할 수 없다.
fun disable()
// 이 함수는 열려있다. 하위 클래스에서 이 메소드를 오버라이드해도 된다.
open fun animate()
// 이 함수는 상위 클래스에서 선언된 열려있는 메소드를 오버라이드 한다.
// 오버라이드한 메소드는 기본적으로 열려있다.
override fun click()
}
코틀린의 클래스와 메소드는 기본적으로 final이다.
어떤 클래스의 상속을 허용하려면 앞에 open 변경자를 붙여야 한다. 그와 더불어 오버라이드를 허용하고 싶은 메소드나 프로퍼티의 앞에도 open 변경자를 붙여야 한다.
// 오버라이드 금지하기
open class RichButton: Clickable {
// 여기 있는 'final'은 쓸데 없이 붙은 중복이 아니다.
// final이 없는 override 메소드나 프로퍼티는 기본적으로 열려있다.
final override fun click()
}
기반 클래스나 인터페이스의 멤버를 오버라이드하는 경우 그 메소드는 기본적으로 열려있다. 오버라이드하는 메소드의 구현을 하위 클래스에서 오버라이드하지 못하게 금지하려면 오버라이드하는 메소드 앞에 final을 명시해야 한다.
열린 클래스와 스마트 캐스트
클래스의 기본적인 상속 가능 상태를 final로 함으로써 얻을 수 있는 큰 이익은 다양한 경우에 스마트 캐스트가 가능하는 점이다.
스마트 캐스트는 타입 검사 뒤에 변경될 수 없는 변수에만 적용 가능하다. 클래스 프로퍼티의 경우 이는 val이면서 커스텀 접근자가 없는 경우에만 스마트 캐스트를쓸 수 있다는 의미이다. 이 요구 사항은 또한 프로퍼티가 final이어야만 한다는 뜻이기도 하다.
프로퍼티가 final이 아니라면 그 프로퍼티를 다른 클래스가 상속하면서 커스텀 접근자를 정의함으로써 스마트 캐스트의 요구 사항을 깰 수 있다.
프로퍼티는 기본적으로 final이기 때문에 따로 고민할 필요 없이 대부분의 프로퍼티를 스마트 캐스트에 활용할 수 있다. 이는 코드를 더 이해하기 쉽게 한다.
// 추상 클래스 정의하기
// 이 클래스는 추상 클래스다. 이 클래스의 인스턴스를 만들 수 없다.
abstract class Animated {
// 이 함수는 추상 함수다. 이 함수에는 구현이 없다. 하위 클래스에서는 이 함수를 반드시 오버라이드해야 한다.
abstract fun animate()
// 추상 클래스에 속했더라도 비추상 함수는 기본적으로 파이널이지만 원한다면 open으로 오버라이드 허용이 가능
open fun stopAnimating() { }
fun animateTwice() { }
}
abstract로 선언한 추상 클래스는 인스턴스화할 수 없다. 추상 클래스에는 구현이 없는 추상 멤버가 있기 때문에 하위 클래스에서 그 추상 멤버를 오버라이드해야만 하는 게 보통이다.
추상 멤버는 항상 열려있다. 따라서 추상 멤버 앞에 open 변경자를 명시할 필요가 없다.
변경자 | 이 변경자가 붙은 멤버는… | 설명 |
---|---|---|
final | 오버라이드할 수 없음 | 클래스 멤버의 기본 변경자 |
open | 오버라이드할 수 있음 | 반드시 open을 명시해야 오버라이드할 수 있다. |
abstract | 반드시 오버라이드해야 함 | 추상 클래스의 멤버에만 이 변경자를 붙일 수 있다. 추상 멤버에는 구현이 있으면 안 된다. |
override | 상위 클래스나 상위 인스턴스의 멤버를 오버라이드하는 중 | 오버라이드하는 멤버는 기본적으로 열려있다. 하위 클래스의 오버라이드를 금지하려면 final을 명시해야 한다. |
참고 서적 : Kotlin IN Action