[Kotlin]인터페이스에 선언된 프로퍼티 구현/프로퍼티 게터 세터
인터페이스에 선언된 프로퍼티 구현
코틀린에서는 인터페이스에 추상 프로퍼티를 선언할 수 있다.
interface User{
val nickname: String
}
이 인터페이스를 구현하는 세 클래스를 만들어보자. 이 세 클래스는 각각 다른 방식으로 추상 프로퍼티 nickname을 구현한다.
// 주 생성자에있는 프로퍼티
class PrivateUser(override val nickname: String) : User
// 커스텀 게터(nickname은 매번 호출될 때마다 substringBefore를 호출해 계산)
class SubscribingUser(val email: String) : User{
override val nickname: String
get() = email.substringBefore("@")
}
// 프로퍼티 초기화 식(객체 초기화 시 계산한 데이터를 뒷받침하는 필드에 저장했다가 불러옴)
class FacebookUser(val accountId: Int) : User {
override val nickname = getFacebookName(accountId)
}
>> println(PrivateUser("corns@naver.com").nickname)
corns@naver.com
>> println(SubscribingUser("corns@naver.com").nickname)
corns
User의 추상 프로퍼티를 구현하고 있으므로 프로퍼티 앞에 override를 표시해야 한다.
인터페이스에는 추상 프로퍼티뿐 아니라 게터와 세터가 있는 프로퍼티를 선언할 수도 있다. 물론 그런 게터와 세터는 뒷받침하는 필드를 참조할 수 없다. (인터페이스는 상태를 저장할 수 없다.)
interface User {
val email: String
// 프로퍼티에 뒷받침하는 필드가 없다. 대신 매번 결과를 계산해 돌려준다.
val nickname: String
get() = email.substringBefore("@")
}
하위 클래스는 추상 프로퍼티인 email을 반드시 오버라이드해야 한다. 반면 nickname은 오버라이드하지 않고 상속할 수 있다.
게터와 세터에서 뒷받침하는 필드에 접근
프로퍼티에 저장된 값의 변경 이력을 로그에 남기는 로직을 짜보자.
이런 경우 변경 가능한 프로퍼티를 정의하되 세터에서 프로퍼티 갑을 바꿀 때마다 약간의 코드를 추가로 실행해야 한다.
class User(val name: String){
var address: String = "unspecified"
set(value: String){
println("""
Address was changed for $name:
"$field" -> "$value".""".trimIndent()) // 뒷받침하는 필드 값 읽기
// 뒷받침하는 필드 값 변경하기
field = value
}
}
>>> val user = User("Corn")
>>> user.address = "Incheon"
Address was chaned for Corn:
"unspecified" -> "Incheon".
user.address = "new value"는 내부적으로 address의 세터를 호출한다. 이 예제에서는 커스텀 세터를 정의해서 추가 로직을 실행한다.
접근자 본문에서 field라는 특별한 식별자를 사용하는데, 게터에서는 field 값을 읽을 수만 있고, 세터에는 field 값을 읽거나 쓸 수 있다.
접근자의 가시성 변경
접근자의 가시성은 기본적으로 프로퍼티의 가시성과 같다. 하지만 원한다면 get이나 set앞에 가시성 변경자를 추가해서 접근자의 가시성을 변경할 수 있다.
// 비공개 세터가 있는 프로퍼티 선언하기
class LengthCounter{
// 이 클래스 밖에서 이 프로퍼티의 값을 바꿀 수 없다.
var counter: Int = 0
private set
fun addWord(word: String){
counter += word.length;
}
}
이 클래스는 자신에게 추가된 모든 단어의 길이를 합산하는데. 외부 코드에서 단어 길이의 합을 마음대로 바꾸지 못하게 내부에서만 변경할 수 있도록 세터의 가시성을 private으로 지정했다.
>>> val lengthCounter = LengthCounter()
>>> lengthCounter.addWord("Hi");
>>> println(lengthCounter.counter)
3
// setter가 pirvate이기 때문에 불가함(컴파일 오류)
>>> lengthCounter.counter = 5