'Effective Java'라는 책에서는 "상속을 위한 설계와 문서를 갖추거나, 그럴 수 없다면 상속을 금지하라"라는 조언이 있습니다. 이는 특별히 하위 클래스에서 오버라이드하게 의도된 클래스와 메서드가 아니라면 모두 final로 만들라는 뜻입니다.
코틀린도 마찬가지 철학을 따릅니다. 자바의 클래스와 메소드는 기본적으로 상속에 대해 열려있지만 코틀린의 클래스와 메서드는 기본적으로 final입니다. 즉 아무런 변경자없이 클래스를 선언했다면 final이기에 클래스를 상속할 수 없습니다.
따라서 어떤 클래스의 상속을 허용하려면 클래스 앞에 open 변경자를 붙여야 합니다. 그와 더불어 오버라이드를 허용하고 싶은 메서드나 프로퍼티의 앞에도 open 변경자를 붙여야 합니다.
interface Clickable {
fun click()
}
// open 변경자를 사용하여 이 클래스는 열려 있습니다. 다른 클래스가 이 클래스를 상속할 수 있습니다.
open class RichButton : Clickable {
// 이 함수는 파이널입니다. 다른 클래스가 이 메서드를 오버라이드 할 수 없습니다.
fun disable() {}
// open을 사용하여 이 함수는 열려있습니다.
// 하위 클래스에서 이 메서드를 오버라이드해도 됩니다.
open fun animate() {}
// 이 함수는 (상위 클래스에 선언된) 열려있는 메소드를 오버라이드합니다.
// 오버라이드한 메서드는 기본적으로 열려있습니다.
override fun click() {}
}
disable() : 클래스에 open 변경자를 넣어도 메서드에 open이 선언되어 있지 않으면 하위 클래스에서 해당 메서드를 오버라이드 할 수 없습니다.
animate() : 메서드에 open을 넣어서 하위 클래스에서 해당 메서드를 오버라이드할 수 있습니다.
click() : 인터페이스에 선언된 메서드를 오버라이드한 것인데 open이 선언되어 있지 않아도 기본적으로 오버라이드한 메서드는 열려있기에 하위 클래스에서 다시 오버라이드할 수 있습니다.
만약 오버라이드하는 메서드의 구현을 하위 클래스에서 오버라이드하지 못하게 금지하려면 오버라이드 메서드 앞에 final을 명시해야 합니다.
코틀린에서도 자바처럼 abstract로 선언할 수 있습니다. abstract로 선언한 추상 클래스는 인스턴스화할 수 없습니다. 추상 클래스에는 구현이 없는 추상 멤버가 있기 때문에 하위 클래스에서 그 추상 멤버를 오버라이드해야만 하는게 보통입니다. 추상 멤버는 항상 열려있기에 추상 멤버 앞에 open 변경자를 명시할 필요가 없습니다.
// abstract 변경자를 사용하여 이 클래스는 추상 클래스
// 인스턴스 만들 수 없다
abstract class Animated {
// 이 함수는 추상 함수
// 하위 클래스에서 이 함수를 반드시 오버라이드 해야함
abstract fun animate()
// 추상 클래스에 속해도 비추상 함수는 기본적으로 파이널이지만
// 원한다면 open으로 오버라이드를 허용함
open fun stopAnimating() {
println("Stop animating~~")
}
// 파이널 함수(오버라이드 못함)
fun animateTwice() {
println("Animate twice~~~")
}
}
animate() : abstract 변경자가 있기에 이 클래스는 하위 클래스에서 무조건 오버라이드 되야합니다.
stopAnimating() : 구현을 가지는 함수인데 open을 넣었기에 하위 클래스에 오버라이드를 허용합니다.
animateTwice() : 구현을 가지는 함수인데 기본적으로 final이기에 하위 클래스에서 오버라이드할 수 없습니다.
변경자 | 이 변경자가 붙은 멤버 | 설명 |
---|---|---|
final | 오버라이드할 수 없음 | 클래스 멤버의 기본 변경자(아무것도 선언 안함) |
oepn | 오버라이드할 수 있음 | 반드시 open을 명시해야 오버라이드할 수 있음 |
abstract | 반드시 오버라이드해야 함 | 추상 클래스의 멤버에만 이 변경자를 붙일 수 있다. 추상 멤버에는 구현이 있으면 안된다. |
override | 상위 클래스나 상위 인스턴스의 멤버를 오버라이드하는 중 | 오버라이드하는 멤버는 기본적으로 열려있다. 하위 클래스의 오버라이드를 금지하려면 final을 명시 |
위에 있는 표는 클래스 멤버에 대해 적용할 수 있고 인터페이스 멤버의 경우 final, open, abstract를 사용하지 않습니다. 인터페이스 멤버는 항상 열려 있으며 final로 변경할수 없습니다. 인터페이스 멤버에게 본문이 없으면 자동으로 추상 멤버가 되지만, 그렇더라도 따로 멤버 선언 앞에 abstract 키워드를 덧불일 필요가 없습니다.
참조
Kotlin in Action
틀린 부분은 댓글로 남겨주시면 수정하겠습니다..!!