[Kotlin in Action] 14. 코틀린의 가시성 변경자 (open, abstract, internal)

반응형
728x90
반응형

코틀린에서의 final

자바에서는 final 로 명시적으로 상속을 금지하지 않는 모든 클래스를 다른 클래스가 상속할 수 있다. 이렇게 기본적으로 상속이 가능하면 편리한 경우도 많지만 문제가 생기는 경우도 많다.

취약한 기반 클래스(fragile base class)
하위 클래스가 기반 클래스에 대해 가졌던 가정이 기반 클래스를 변경함으로써 깨져버린 경우에 생긴다.
어떤 클래스가 자신을 상속하는 방법에 대해 정확한 규칙을 제공하지 않는다면 그 클래스의 클라이언트는 기반 클래스를 작성한 사람의 의도와 다른 방식으로 메서드를 오버라이드할 위험이 있다.
모든 하위 클래스를 분석하는 것은 불가능 하므로 기반 클래스를 변경하는 경우 하위 클래스의 동작이 예키지않게 바뀔 수도 있다는 면에서 기반 클래스는 취약하다.

"상속을 위한 설계와 문서를 갖추거나, 그럴 수 없다면 상속을 금지하라."
이는 하위클래스에서 오버라이드하게 의도된 클래스와 메서드가 아니라면 모두 final로 만들라는 뜻이다.

코틀린도 마찬가지다.
자바의 클래스와 메서드는 기본적으로 상속에 열려있지만 코틀린의 클래스와 메서드는 기본적으로 final이다.
어떤 클래스의 상속을 허용하려면 클래스 앞에 open 변경자를 붙여야한다.
오버라이드를 허용하고 싶은 메서드나 프로퍼티의 앞에도 open 변경자를 붙여야한다.

 

Clickable.kt
package chapter4_클래스_객체_인터페이스._2_변경자._open

interface Clickable {
    fun click()
    fun showOff() = println("I'm clicked") // default 구현이 있는 메서드
}

 

RickButton.kt
package chapter4_클래스_객체_인터페이스._2_변경자._open

open class RichButton : Clickable { // 이 클래스는 열려있다. 다른 클래스가 이 클래스를 상속할 수 있다.
    fun disable() {}  // default final 이다. 하위 클래스가 이 메서드를 오버라이드 할 수 없다.
    open fun animate() {} // 하위클래스가 이 메서드를 오버라이드 할 수 있다.
    final override fun click() {} // 이 함수는 상위 클래스의 열려있는 메서드를 오버라이드한다. 오버라이드한 메서드는 기본적으로 열려있다.
}

 

1) open class 클래스명

open class RichButton : Clickable {

이 클래스는 다른 클래스가 상속할 수 있다.

 

2) 아무것도 선언되어있지 않은 default 는 final 이다.

fun disable() {}

 

3) open fun 메서드명

open fun animate() {}

하위 클래스가 이 메서드를 오버라이드 할 수 있다.

 

4) final override fun 메서드명

final override fun click() {}

final 이 없는 override 메서드나 프로퍼티는 기본적으로 열려있다. 따라서 오버라이드 하지못하게 금지하려면 오버라이드하는 메서드 앞에 final을 명시해야한다.

 

 

abstract

추상클래스를 생성한다.

 

Anumated.kt
abstract class Animated { // 추상클래스다. 이 클래스의 인스턴스를 만들 수 있다.
    abstract fun animated() // 이 함수는 추상 함수다. 이 함수에는 구현이 없다. 하위 클래스에서는 반드시 오버라이드해야한다.
    // 추상클래스에 속했더라도 비추상함수는 기본적으로 파이널이지만 원한다면 open 으로 오버라이드 허용할 수 있다.
    open fun stopAnimating() {}
    fun animateTwice() {}
}

 

1) abstract class 클래스명

추상클래스다.

 

2) abstract fun 메서드명

추상 함수다. 이 함수에는 구현이 없으며, 하위 클래스에서는 반드시 오버라이드 해야하는 메서드다.

 

3) oepn fun 메서드명

추상클래스에 속했더라도, 비추상함수는 기본적으로 final 이다. 하지만 원한다면 open 을 사용하여 오버라이드를 허용할 수 있다.

 

인터페이스 멤버의 경우
인터페이스 멤버의 경우 final, open, abstract 를 사용하지 않는다.인터페이스 멤버는 항상 열려있으며 final 로 변경할 수 없다.인터페이스 멤버에게 본문이 없으면 자동으로 추상 멤버가 되지만, 그렇더라도 따로 멤버 선언 앞에 abstract 키워드를 덧붙일 필요가 없다.

 

클래스 내의 상속 제어 변경자 의미

변경자 이 변경자가 붙은 멤버는 설명
final 오버라이드 불가능 클래스 멤버의 기본 변경자다.
open 오버라이드 가능 반드시 open을 명시해야 오버라이드 가능하다.
abstract 반드시 오버라이드 필수 추상 클래스의 멤버에만 이 변경자를 붙일 수 있다.
추상 멤버에는 구현이 있으면 안된다.
override 상위 클래스나 상위 인스턴스의 멤버를 오버라이드하는 중 오버라이드하는 멤버는 기본적으로 open이다.
하위 클래스의 오버라이드를 금지하려면 final을 명시해야한다.

 

 

가시성 변경자의 도입

코드 기반에 있는 선언에 대한 클래스 외부 접근을 제어한다.
어떤 클래스의 구현에 대한 접근을 제한함으로써 그 클래스에 의존하는 외부 코드를 깨지 않고도 클래스 내부 구현을 변경할 수 있다.

자바와 같은 public, protected, private 변경자가 있다.
하지만 코틀린의 기본 가시성은 자바와 다르다. 아무 변경자도 없는 경우 선언은 모두 공개(public) 된다.

package-private
자바의 기본 가시성인 패키지 전용은 코틀린에 없다.
코틀린은 패키지를 namespace를 관리하기 위한 용도로만 사용한다.그래서 패키지를 가시성 제어에 사용하지 않는다.

패키지 전용 가시성에 대한 대안으로 코틀린에는 internal 이라는 새로운 가시성 변경자를 도입했다.

 

 

internal

internal은 "모듈 내부에서만 볼 수 있다."
모듈은 한번에 한꺼번에 컴파일되는 코틀린 파일들을 의미한다. (인텔리J, 이클립스, 메이븐, 그레이들 등의 프로젝트가 모듈이 될 수 있다.)

모듈 내부 가시성은 여러분의 모듈의 구현에 대해 진정한 캡슐화를 제공한다는 장점이 있다.

* JAVA
패키지가 같은 클래스를 선언하기만 하면 어떤 프로젝트의 외부에 있는 코드라도 패키지 내부에 있는 패키지 전용 선언에 쉽게 접근할 수 있다. 그래서 모듈의 캡슐화가 쉽게 깨진다.

다른 차이는 코틀린에서는 최상위 선언 (클래스, 함수, 프로퍼티 ) 에 대해 private 가시성을 허용한다.
비공개 가시성인 최상위 선언은 그 선언이 들어있는 파일 내부에서만 사용할 수 있다. 이 또한 하위 시스템의 자세한 구현 사항을 외부에 감추고 싶을때 유용한 방법이다.

 

코틀린의 가시성 변경자

변경자 클래스 멤버 최상위 선언
public (기본 가시성) 모든 곳에서 볼 수 있다. 모든 곳에서 볼 수 있다.
internal 같은 모듈 안에서만 볼 수 있다. 같은 모듈 안에서만 볼 수 있다.
protected 하위 클래스 안에서만 볼 수 있다. (최상위 선언에 적용할 수 없음)
private 같은 클래스 안에서만 볼 수 있다. 같은 파일 안에서만 볼 수 있다.

 

Focusable.kt
interface Focusable {
    fun setFocus(b: Boolean) = println("I ${if (b) "got" else "lost"} focus.")
    fun showOff() = println("I'm focusable!")
}

 

TalkativeButton.kt
internal open class TalkativeButton : Focusable {
    private fun yell() = println("Hey!")
    protected fun whisper() = println("Let's talk!")
}

 

코틀린은 public 함수인 giveSpeech 안에서 그보다 가시성이 더 낮은 타입인 TalkativeButton을 참조 못하게한다. 어떤 클래스의 기반 타입 목록에 들어있는 타입이나 제네릭 클래스의 타입 파라미터에 들어있는 타입의 가시성은 그 클래스 자신의 가시성과 같거나 높아야한다.


메서드의 시그니처에 사용된 모든 타입의 가시성은 그 메서드의 가시성과 같거나 더 높아야 한다. 이런 규칙은 어떤 함수를 호출하거나 어떤 클래스를 확장할때 필요한 모든 타입에 접근할 수 있게 보장해준다.

fun TalkativeButton.giveSpeech() { // 오류 : "public" 멤버가 자신의 "internal" 수신 타입인 "TalkativeButton"를 노출함
    yell() // yell 에 접근할 수 없음 : yell 은 TalkativeButton의 private 멤버다.
    whisper() // whisper 에 접근할 수 없음 : whisper 은 TalkativeButton 의 protected 멤버다.
}


여기서 컴파일 오류를 없애려면, giveSpeech 확장 함수의 가시성을 internal 로 바꾸거나, TalktiveButton 클래스의 가시성을 public 으로 바꿔야한다.

internal fun TalkativeButton.giveSpeech() { // 오류 : "public" 멤버가 자신의 "internal" 수신 타입인 "TalkativeButton"를 노출함

}

 

 

코틀린의 가시성 규칙

  • 자바에서는 같은 패키지 안에서 protected 멤버에 접근할 수 있지만, 코틀린에서는 그렇지 않다.
    • protected 멤버는 오직 어떤 클래스나 그 클래스를 상속한 클래스 안에서만 보인다.
  • 클래스를 확장한 함수는 그 클래스의 private이나 protected 멤버에 접근할 수 없다.
  • 코틀린에서는 외부 클래스가 내부 클래스나 중첩된 클래스의 private 멤버에 접근할 수 없다.

 

 

반응형

Designed by JB FACTORY