본문 바로가기
Language/Kotlin

[Kotlin IN ACTION] - Kotlin 접근 제어자(가시성 변경자) / 내부 클래스 / 중첩 클래스

by Jman 2022. 7. 25.

가시성 변경자 (접근 제어자)

코드 기반에 있는 선언에 대한 클래스 외부 접근을 제어한다.

어떤 클래스의 구현에 대한 접근을 제한함으로써 그 클래스에 의존하는 외부 코드를 깨지 않고도 클래스 내부 구현이 변경 가능하다.

 

Kotlin 접근 제어자 종류

  • public(기본 가시성)
  • internal
  • protected
  • private

아무 변경자가 없는 경우는 public 이다.

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

 

Kotlin 과 Java의 차이

  • 자바의 기본 가시성인 패키지 전용(package-private)이 없다. 코틀린은 패키지를 네임스페이스를 관리하기 용도로만 사용한다.
  • 패키지 전용 가시성에 대한 대안으론 interanl 이라는 새로운 가시성이 존재한다. (internal : 모듈 내부에서만 볼수 있음)
  • 최상위 선언에 대해 private 을 허용 한다는 점.
  • 자바에서는 같은 패키지 안에서 protected 멤버에 접근 가능하지만, 코틀린에서는 그렇지 않다. 코틀린은 오직 어떤 클래스나 그 클래스를 상속한 클래스 안에서만 보인다.
  • 클래스를 확장한 함수는 그 클래스의 private 이나 protected 멤버에 접근할 수 없다.
  • 코틀린에서는 외부 클래스가 내부 클래스나 중첩된 클래스의 private 멤버에 접근할 수 없다.

 

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

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

 

예시 코드를 통해서 개념을 이해해보자.

// 직렬화 할 수 있는 State(상태)가 있는 뷰 선언
interface State : java.io.Serializable

interface View {
    fun getCurrentState() : State
    fun reStoreState (State: State) {}
}

 

위 코드를 구현한 코틀린과 자바 코드를 가지고 비교해보자.

 

[Java 코드]

// Java 에서 내부 클래스를 사용해 View 구현해보기
public class Button implements View {
	@Override
    public State getCurrentState() {
    	return new ButtonState(); // ButtonState의 새 인스턴스로 만든다.
    }
    
    @Override
    public void reStoreState(State state) {
    	...
    }
    
    // State 인터페이스를 구현한 클래스를 정의해서 Button에 대한 구체적인 정보를 저장한다.
    public class ButtonState implements State {
    	....
    }
}

위 자바 코드는 잘못 됐다. 무엇이 잘못 됐을까?

선언한 버튼의 상태를 직렬화하면 java.io.NotSerializableException : Button 이라는 오류가 발생한다.

 

자바에서 다른 클래스 안에 정의한 클래스는 자동으로 내부 클래스가 된다는 사실을 알 것이다.

ButtonState 클래스는 바깥쪽 Button 클래스에 대한 참조를 묵시적으로 포함하게 된다.

 

그 참조로 인해 ButtonSate의 직렬화를 방해한다.

 

이 문제를 해결하기 위해선, ButtonState 클래스에 static 클래스를 선언해야 한다.

그러면 중첩 클래스로 인지를 해서, ButtonState 클래스를 둘러싼 Button 클래스에 대한 묵시적인 참조가 사라지게 된다.

 

코틀린 코드는 정반대이다. 코틀린 코드를 확인해보자.

 

[Kotlin]

// Kotlin 에서 중첩 클래스를 사용해 코틀린에서 View 구현하기
class Button : View {
    override fun getCurrentState(): State  = ButtonState()

    override fun reStoreState(State: State) {
        super.reStoreState(State)
    }

    class ButtonState : State {
        //...
    }
}

 

코틀린 중첩 클래스에 아무런 변경자가 붙지 않는다면, 자바 static 중첩 클래스와 같다.

이를 내부 클래스로 변경해서 바깥쪽 클래스에 대한 참조를 포함하게 만들고 싶다면? inner 변경자를 붙이면 된다.

 

클래스 B안에 정의된 클래스 A 자바에서 코틀린에서
중첩 클래스 static class A class A
내부 클래스 class A inner class A

 

코틀린에서 바깥쪽 클래스의 인스턴스를 가리키는 참조를 표기하는 방법도 자바와 다르다.

내부 클래스 Inner 안에서 바깥쪽 클래스 Outer의 참조에 접근하려면 this@Outer 라고 작성해야 한다.

 

Kotlin IN ACITON 155p

class Outer {
	inner class Inner {
    	fun getOuterReference() : Outer = this@Outer
    }
}