본문 바로가기

Dev Book/Kotlin IN ACTION

CH4(4.1). 클래스 계층 정의

1. 코틀린 인터페이스

- 코틀린 인터페이스 안에는 추상 메소드뿐 아니라 구현이 있는 메소드도 정의할 수 있다. implements를 쓰는 자바와는 다르게, 클래스 상속과 마찬가지로 콜론(:)을 붙이고 인터페이스 이름을 적는 것으로 구현을 처리한다. 자바와 똑같이 인터페이스는 다중 구현이 가능하고, 클래스는 오직 하나만 확장할 수 있다. 

- 자바의 @overried 애노테이션과 비슷한 overried 변경자가 있다. 하지만 이는 상위 클래스, 인터페이스에 있는 프로퍼티나 메소드를 오버라이드 할 때 꼭 사용해야 한다. 

- 인터페이스 메소드도 디폴트 구현을 제공할 수 있다. 이 경우 그냥 메소드 본문을 메소드 시그니쳐 뒤에 추가하면 된다. 만약 한 클래스가 같은 메소드에 대해서 서로 다른 디폴트 구현이 있는 두 인터페이스를 함께 구현한다면, 어느쪽도 선택되지 않고 컴파일러 오류가 발생한다. 이 경우 두 메소드를 아우르는 오버라이딩 메소드를 하위 클래스에 직접 구현해야 한다.

 

class Button : Clickable, Focusable {
	override fun click() = println("I was clicked")
    override fun showOff() {
    	super<Clickable>.showOff()
        super<Focusable>.showOff() //둘 이상의 똑같은 디폴트 구현이 있는 경우, 하위클래스에서 명시적으로 새로운 구현을 제공해야 함.
    }
}

 

2. open, final, abstract 변경자

- 취약한 기반 클래스 문제.

상속을 위한 설계와 문서를 갖추거나, 그럴 수 없다면 상속을 금지하라. 「Effective JAVA」

 

- 코틀린은 위와 같은 철학에 따라 클래스와 메소드를 기본적으로 final로 지정한다. 어떤 클래스의 상속을 허용하려면 클래스 앞에 open 변경자를 붙여야 한다. 마찬가지로, 오버라이드를 허용하고 싶은 메소드, 프로퍼티 앞에서 open 변경자를 붙여야 한다.

 

open class RichButton : Clickable { //다른 클래스가 해당 클래스 상속 가능.
	fun disable(){} //기본 final. 하위클래스가 이 메소드 오버라이드 불가.
    open fun animate(){} //open. 하위클래스가 이 메소드 오버라이드 가능.
    override fun click(){} //오버라이드한 메소드는 기본적으로 열려있음. 하위클래스에서 이 메소드 오버라이드 가능.
}

 

- 기반 클래스나 인터페이스 멤버를 오버라이드 하는 경우,그 메소드는 기본적으로 열려있다. 따라서 오버라이드 하지 못하게 금지하려면 오버라이드하려는 메소드 앞에 final을 명시해야 한다.

- 코틀린에서도 클래스를 abstract 선언할 수 있다. abstract 선언한 추상 클래스는 인스턴스화할 수 없으며, 하위클래스에서 그 추상멤버를 오버라이드해야 한다. 따라서 추상 멤버는 항상 열려있고 별도로 open 변경자를 명시할 필요없다.

- 상속 제어 변경자

 

변경자 설명
final 오버라이드할 수 없음
open 오버라이드할 수 있음. 
abstract 추상 클래스의 멤버에만 붙일 수 있음. 반드시 오버라이드 해야 함.
override 오버라이드하는 중. 오버라이드하는 멤버는 기본적으로 열려있음.

 

3. 가시성 변경자

- 코틀린의 기본 가시성은 자바와 다르게 아무 변경자도 없는 경우 모두 공개된다.(public) 자바의 기본 가시성인 패키지 전용은 코틀린에 없고, 대안으로 internal을 사용한다. 이는 모듈 내부에서만 볼 수 있음을 나타낸다. 또 하나의 자바와의 차이점은 코틀린에서는 최상위 선언에 대해 private 가시성을 허용한다는 점이다. 

 

변경자 클래스 멤버 최상위 선언
public 모든 곳에서 볼 수 있음 모든 곳에서 볼 수 있음
internal 같은 모듈 안에서만 볼 수 있음 같은 모듈 안에서만 볼 수 있음
protected 하위 클래스 안에서만 볼 수 있음 최상위 선언에 적용 불가
private 같은 클래스 안에서만 볼 수 있음 같은 파일 안에서만 볼 수 있음

 

- 어떤 클래스의 기반 타입 목록에 들어있는 타입이나 제네렉 클래스의 타입 파라미터에 들어있는 타입의 가시성은 그 클래스 자신의 가시성과 같거나 더 높아야 하고, 메소드의 시그니처에 사용된 모든 타입의 가시성은 그 메소드의 가시성과 같거나 더 높아야 한다는 더 일반적인 규칙을 따른다.

4. 내부 클래스와 중첩된 클래스

- 코틀린의 중첩 클래스는 명시적으로 요청하지 않는 한 바깥쪽 클래스 인스턴스에 대한 접근 권한이 없다. 

- 코틀린 중첩 클래스에 아무런 변경자가 붙지 않으면 자바 static 중첩 클래스와 같다. 이를 내부 클래스로 변경해서 바깥쪽 클래스에 대한 참조를 포함하게 만들고 싶다면 inner 변경자를 붙여야 한다. 만약 내부 클래스 Inner 안에서 바깥쪽 클래스 Outer의 참조에 접근하려면 this@Outer라고 써야한다.

5. 봉인된 클래스 

 

sealed class Expr {
	class Num(val value:Int) : Expr()
    class Sum(val left:Expr, val right:Expr): Expr()
}

fun eval(e:Expr):Int = 
	when(e) {
    	is Expr.Num -> e.value
        is Expr.Sum -> eval(e.right) + eval(e.left)
    }

 

- 상위 클래스에 sealed 변경자를 붙이면 그 상위 클래스를 상속한 하위 클래스 정의를 제한할 수 있다. sealed 클래스의 하위 클래스를 정의할 때는 반드시 상위 클래스 안에 중첩시켜야 한다. sealed로 표시된 클래스는 자동으로 open이다. 이를 이용하면 sealed 클래스에 속한 값에 대해 디폴트 분기(else를 이용한)를 사용하지 않고 when식을 사용하면 나중에 클래스 상속 계층에 새로운 하위클래스를 추가해도 when식이 컴파일 되지 않아 심각한 오류를 피할 수 있다.(새로운 클래스 처리를 하지 않고 디폴트 분기로 이어지는 것에 따라 발생할 수 있는)