Kotlin ‐ 상속[Effective Kotlin Item 10] - thought-corner/Backend-PlayGround GitHub Wiki
구성 요소 오버라이딩
- 코틀린에서 기본적으로 서브 클래스는 슈퍼 클래스에 정의된 구성 요소를 오버라이딩 할 수 없다.
- 코틀린의 모든 요소는 기본적으로 닫혀 있기 때문에 오버라이딩하려면
open제어자를 사용해 명시적으로 허용해야 한다. - 그리고 서브 클래스에서 오버라이딩하는 요소에 대해
override제어자를 추가해야 한다.
open class Mammal { // open으로 명시
val canFeed = false
open fun feedYoung() {
if (canFeed) {
println("Feeding young with mulk")
}
}
}
class Cat : Mammal() {
override fun feedYoung() { // override 제어자 사용
if (canFeed) {
// ...
} else {
// ...
}
}
}
비어 있지 않은 생성자가 있는 부모
- 슈퍼 클래스 생성자가 매개변수를 받으면 괄호 안에 적절한 인수를 추가하여 호출해야 한다.
open class Animal(val name: String)
class Dodo : Animal("Dodo")
super 호출
- 클래스를 상속할 때 슈퍼클래스의 동작을 계승하면서 서브클래스에 특화된 동작을 추가할 수 있다.
open class Dog {
open fun seeFriend() {
println("Wave its tall")
}
}
class BorderCollie : Dog() {
override fun seeFriend() {
println("Lie down")
super.seeFriend()
}
}
fun main() {
val dog = Dog()
dog.seeFriend()
var borderCollie = BorderCollie()
borderCollie.seeFriend()
}
추상 클래스
- 다른 클래스들의 슈퍼 클래스로만 이용하고 객체를 생성할 수 없는 클래스를 정의할 때는 클래스 정의 앞에
abstract키워드를 붙인다. open제어자는 이 클래스로부터 상속이 가능하다는 의미지만,abstract는 이 클래스를 사용하려면 반드시 상속해야 한다는 의미를 가진다.
abstract class Mammal {
val haveHairOrFur = true
val warmBlooded = true
val canFeed = false
fun feedYoung() {
if (canFeed) {
println("Feeding young with milk")
}
}
}
인터페이스
- 인터페이스는 클래스가 제공해야 할 프로퍼티와 메서드를 정의한다.
- 인터페이스는
interface키워드와 이름, 그리고 구현해야 할 프로퍼티와 메서드로 이루어진 본문으로 정의한다. - 인터페이스에는 생성자가 없으므로 생성자를 호출하진 않는다.
- 읽기만 가능한
val프로퍼티는 쓰기도 가능한var프로퍼티로 오버라이딩할 수 있다. val프로퍼티에는Getter만 필요하지만var프로퍼티는Getter/Setter모두 있어서 포괄적이기 때문에 가능한 것이다.- 추가로 인터페이스는 메서드를 디폴트 메서드(default method)로 지정할 수 있다.
- 디폴트 메서드는 본문을 가지고 있어서 서브 클래스가 구현하지 않아도 된다. 만약 기본 로직을 변경해야 한다면 오버라이딩을 하면 된다.
interface CoffeeMaker {
val type: String
fun makeCoffee(size: Size): Coffee
}
인터페이스(interface)와 추상 클래스(abstract class)의 결정적 차이는 상태에 있다.
- 인터페이스에 디폴트 메서드가 추가되면서 추상 클래스와의 차이가 모호해 보일 수 있으나 코틀린에서 2가지를 구분하는 중요한 기준은 바로 상태의 저장 여부에 있다.
- 추상 클래스는 내부에 데이터를 저장할 수 있는 상태를 가질 수 있지만, 인터페이스는 상태를 저장할 수 없다.
가시성
- 클래스는 최소한의 정보만 노출하도록 설계되어야 한다.
- 공개할 이유가 없다면 숨기는 것이 맞다.
- 제한이 덜한 가시성 타입을 사용할 합당한 이유가 없다면 클래스와 요소의 가시성을 최대한 제한해야 한다. 코틀린에서는 가시성 제어자를 사용하여 이 원칙을 설계에 투영한다.
| 제어자 | 특징 |
|---|---|
| public(기본) | 선언된 클래스를 볼 수 있는 클라이언트 전부가 볼 수 있다. |
| private | 클래스 내부에서만 볼 수 있다. |
| protected | 클래스와 서브 클래스에서 볼 수 있다. |
| internal | 선언된 클래스를 볼 수 있는 클라이언트를 포함하는 모듈 내부에서 볼 수 있다. |
Any
- 코틀린의 클래스 계층구조 최상위에는
Any클래스가 있다. - 슈퍼클래스를 명시하지 않은 모든 클래스의 슈퍼 클래스는 암묵적으로
Any가 되며, 매개변수를Any?으로 선언하면 어떠한 객체라도 인수로 받겠다는 뜻이다. Any에 정의된 메서드들은 오픈되어 있는 디폴트 메서드이므로 필요하다면 오버라이딩할 수 있다.
fun consumeAnything(a: Any?) {
println("Om nom $a")
}
Any 클래스 3대 규약
Any클래스에는equals(),hashCode(),toString()3가지 메서드가 정의되어 있다. 이 메서드들을 오버라이딩할 때는 반드시 지켜야 하는 논리적 규약이 있다.- 이를 어기면
Set이나Map같은 컬렉션에서 객체를 잃어버리는 치명적인 버그가 발생한다.