본문 바로가기

Dev Book/Kotlin IN ACTION

CH4(4.2). 뻔하지 않은 생성자와 프로퍼티를 갖는 클래스

1. 클래스 초기화

class User(val nickname:String)

 

- 다음과 같이 클래스 이름 뒤에 오는 괄호로 둘러싸인 코드를 주 생성자라고 한다. 주 생성자는 생성자 파라미터를 지정하고 그 생성자 파라미터에 의해 초기화되는 프로퍼티를 정의하는데 쓰인다.

- 함수 파라미터와 마찬가지로 생성자 파라미터에도 디폴트 값을 정의할 수 있다. 

- 클래스에 기반 클래스가 있다면 주 생성자에서 기반 클래스의 생성자를 호출해야 한다. 기반 클래스를 초기화 하려면 기반 클래스 이름 뒤에 괄호를 치고 생성자 인자를 넘긴다. 인자가 없는 기반 클래스를 상속해도 하위 클래스는 반드시 기반 클래스의 생성자를 호출해야 하므로 꼭 빈 괄호가 들어가야 한다.

- 인터페이스는 생성자가 없기 때문에, 어떤 클래스가 인터페이스를 구현하는 경우 그 클래스의 상위 클래스 목록에 있는 인터페이스 이름 뒤에는 아무 괄호가 없다. 

- 어떤 클래스를 클래스 외부에서 인스턴스화하지 못하게 막고 싶다면 모든 생성자를 private으로 만들면 된다.

 

2. 부 생성자

- 부 생성자는 constructor 키워드로 시작한다. 

- super() 키워드를 통해 자신에 대응하는 상위 클래스 생성자를 호출한다.

- this()를 통해 클래스 자신의 다른 생성자를 호출할 수 있다.

- 클래스에 주 생성자가 없다면 모든 부 생성자는 반드시 상위 클래스를 초기화하거나 다른 생성자에게 생성을 위임해야 한다.

 

class MyButton : View {
	constructor(ctx:Context) : this(ctx, MY_STYLE){
    	//...
    }
    
    constructor(ctx:Context, attr:AttributeSet) : super(ctx, attr) {
    	//...
    }
}

 

3. 인터페이스에 선언된 프로퍼티 구현

- 인터페이스에 추상 프로퍼티 선언을 넣을 수 있다.

 

interface User {
	val nickname : String
}

 

- 이는 User 인터페이스를 구현하는 클래스가 nickname 값을 얻을 수 있는 방법을 제공해야 한다는 뜻이다.

 

class PrivateUser(override val nickname:String) : User

class SubscribingUser(val email:String): User{
	override val nickname:String
    	get() = email.substringBefore('@')
}

class FacebookUser(val accountId:Int): User{
	override val nickname = getFacebookName(accountId)
}

 

- SubscribingUser는 호출될 때마다 nickname을 계산하는 커스텀 게터를 활용하고, FacebookUser는 객체 초기화 시 저장한 데이터를 뒷받침하는 필드에 저장했다가 불러오는 방식을 활용하고 있다.

 

- 인터페이스에는 추상 프로퍼티 뿐 아니라 게터와 세터가 있는 프로퍼티를 선언할 수 있다. 

 

4. 게터와 세터에서 뒷받침하는 필드에 접근

class User(val name:String){
	var address:String = "unspecified"
    	set(value:String) {
        	//...
            
            field = value
        }
}

 

- 접근자의 본문에서는 field라는 특별한 식별자를 통해 뒷받침하는 필드에 접근할 수 있다. 

- 변경가능 프로퍼티의 게터와 세터 중 한쪽만 직접 정의해도 된다.

- 만약 field를 사용하지 않는 커스텀 접근자 구현을 정의한다면 뒷받침하는 필드는 존재하지 않는다.

- 원한다면 get이나 set 앞에 가시성 변경자를 추가해서 접근자의 가시성을 변경할 수 있다.