노트/Java to Kotlin

상속, 인터페이스, 접근 제어

작성

step 10(상속)과 11(접근 제어)에서 적은 것이다.

추상 클래스와 상속

abstract class KotlinAnimal(
    protected val species: String,
    protected open val legCount: Int,   // 추상 프로퍼티가 아니면 상속받아 바꾸려 할 때 open 을 붙인다
) {
    abstract fun move()
}

class KotlinCat(
    species: String
) : KotlinAnimal(species, 4) {

    override fun move() {
        println("고양 고양")
    }
}
  • 상속과 구현 모두 : 를 쓴다. 컨벤션은 : 양쪽에 공백을 둔다.
  • 상위 클래스를 상속할 때 상위 클래스의 생성자를 바로 호출해야 한다.
  • 오버라이드할 때는 override 를 반드시 붙인다.

상속 관련 키워드

  • final: 오버라이드할 수 없다. 기본값이다.
  • open: 오버라이드를 열어 준다.
  • abstract: 반드시 오버라이드해야 한다.
  • override: 상위 타입을 오버라이드하고 있음을 나타낸다.

인터페이스

interface KotlinFlyable {
    // default 키워드 없이 메서드를 구현할 수 있다
    fun act() {
        println("파닥파닥")
    }
}

interface KotlinSwimable {
    fun act() {
        println("어푸어푸")
    }
}

class KotlinPenguin(
    species: String
) : KotlinAnimal(species, 2), KotlinSwimable, KotlinFlyable {

    private val wingCount = 2

    override fun move() {
        println("뒤뚱뒤뚱")
    }

    // 추상 클래스에서 자동으로 만들어진 getter 를 오버라이드하려고 custom getter 를 쓴다
    override val legCount: Int
        get() = super.legCount + this.wingCount

    override fun act() {
        super<KotlinSwimable>.act()
        super<KotlinFlyable>.act()
    }
}
  • 같은 이름의 default 메서드가 두 인터페이스에 있으면 super<타입>.메서드() 로 어느 쪽을 부를지 고른다.
  • 상위 클래스의 생성자나 init 블록에서 open 프로퍼티를 쓰면 하위 클래스가 초기화하기 전 값으로 불려 버그가 난다.

요약

  1. 상속하거나 구현할 때 : 를 쓴다.
  2. 상위 클래스를 상속하면 생성자를 반드시 호출한다.
  3. override 를 필수로 붙인다.
  4. 추상 멤버가 아니면 오버라이드할 수 없다. open 을 써야 가능하다.
  5. 상위 클래스의 생성자나 init 에서 open 프로퍼티를 쓰면 초기화 전 값으로 불린다.

접근 제어

// 생성자에 접근 지시어를 붙이려면 constructor 를 써야 한다
open class Cat protected constructor(val name: String) {
    fun hello() {
        println("Hello, I'm $name")
    }
}

// 유틸성 함수는 파일 최상단에 바로 적는다
fun isDirectoryPath(path: String): Boolean {
    return path.startsWith("/")
}

class Car(
    internal val name: String,
    private val owner: String,
    _price: Int
) {
    var price = _price
        private set   // setter 에만 따로 가시성을 줄 수 있다
}

접근할 수 있는 곳은 이렇다.

  • public: 모든 곳. 기본값이다.
  • protected: 선언된 클래스와 하위 클래스. Java 와 달리 같은 패키지는 빠진다.
  • internal: 같은 모듈 안.
  • private: 선언된 클래스 안. 파일 최상단에 쓰면 같은 파일 안.

Kotlin 은 패키지를 네임스페이스 관리용으로만 쓰고 접근 제어에는 쓰지 않는다. 그래서 protected 에서 패키지가 빠지고, Java 의 default(package-private)는 모듈 단위 접근을 통제하는 internal 로 바뀌었다. protected 는 클래스에 속하는 개념이라 파일 최상단에는 쓸 수 없다.

Java 와 함께 쓸 때 주의할 점

  1. internal 은 바이트코드에서 public 이 된다. Java 코드에서 Kotlin 모듈의 internal 코드를 가져다 쓸 수 있다.
  2. Java 는 같은 패키지에 있는 Kotlin protected 멤버에 접근할 수 있다.

요약

  1. Kotlin 에서 패키지는 네임스페이스 관리용이라 protected 의 의미가 달라진다.
  2. default 가 internal 로 바뀌어 모듈 사이 접근을 통제한다.
  3. 생성자에 접근 지시어를 붙일 때는 constructor 를 명시한다.
  4. 유틸성 함수는 파일 최상단을 쓴다.
  5. 프로퍼티의 custom setter 에 접근 지시어를 붙일 수 있다.
  6. Java 에서 Kotlin 코드를 쓸 때 internal 과 protected 를 주의한다.