[출처 - Kotlin In Action] [아래 내용들은 Kotlin In Action을 공부하면서 스스로 정리한 내용입니다]
인터페이스 프로퍼티
코틀린에서는 인터페이스에 추상 프로퍼티 선언을 할 수 있습니다.
interface User {
val nickname: String
}
위 User 인터페이스를 확장한 클래스는 nickname을 얻을 수 있는 방법을 제공해야합니다.
그리고 이 인터페이스의 프로퍼티 선언에는 필드나 게터 그리고 상태같은 정보가 들어 있지 않기 때문에 필드나 게터, 상태등을 저장 사용하기 위해서는 하위 클래스에서 상속받은 프로퍼티를 만들어야 합니다.
그럼 위의 내용을 기억하면서 그 사용 방법을 확인해 보겠습니다.
interface User {
val nickname: String
}
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)
}
fun main(args: Array<String>) {
println(PrivateUser("test@kotlinlang.org").nickname)
println(SubscribingUser("test@kotlinlang.org").nickname)
println(FacebookUser(100).nickname)
}
결과는 다음과 같습니다.
test@kotlinlang.org
test
fb:100
PrivateUser는 User를 확장하고 주 생성자에 직접 프로퍼티를 생성했습니다. 이 프로퍼티는 User의 nickname, 즉 추상 프로퍼티를 사용하고 있으므로 override를 선언해야 합니다.
SubscribingUser 역시 User를 확장하였습니다. 다른점은 추상 프로퍼티를 사용하고있고, 그에 대한 값은 커스텀 게터로 설정하고 있습니다. 그래서 추상 프로퍼티인 nickname에 email 에 들어온 String 데이터를 subStringBefore 메소드를 통하여 계산하여 그 결과를 설정합니다.
마지막으로 FacebookUser 역시 User를 확장하고 있습니다. 하지만 여기서는 추상 프로퍼티를 초기화 식으로 초기화 하고 있습니다. 사용자 ID(int 값)을 받아서 getFacebookName를 통하여 nickname을 초기화 하고 있습니다.
여기서 SubscribingUser과 FacebookUser의 차이점은
SubscribingUser은 호출 될 때마다 게터를 통하여 nickname 정하고, FacebookUser 는 초기화 할때 nickname을 초기화 한다.
코드로 설명하면
interface User {
val nickname: String
}
class SubscribingUser(var email: String) : User {
override val nickname: String
get() = email.substringBefore('@')
}
class FacebookUser(var accountId: Int) : User {
override val nickname = getFacebookName(accountId)
}
fun main(args: Array<String>) {
val sub = SubscribingUser("test@kotlinlang.org")
println(sub.nickname)
sub.email = "test@kotlinlang.org_2"
println(sub.nickname)
val fac = FacebookUser(100)
println(fac.nickname)
fac.accountId = 200
println(fac.nickname)
}
이렇게 할 경우
결과는
test
test_2
fb:100
fb:100
이렇게 된다는 의미 입니다.
또한, 인터페이스에는 추상 프로퍼티 뿐만 아니라 게터와 세터가 있는 프로퍼티를 선언할 수도 있습니다.
interface User {
val email: String
val nickname: String
get() = email.substringBefore('@')
}
위 인터페이스의 추상 프로퍼티인 email은 하위 클래스에서 반드시 오버라이드 해야하지만 게터가 있는 nickname의 경우에는 오버라이드 하지 않고 상속할 수 있다.
게터와 세터에서 뒷받침하는 필드에 접근
위에서 값을 저장하는 프로퍼티와 게터를 통해 계산된 값을 받는 프로퍼티를 확인했습니다. 이번에는 이 2가지를 사용하여 값을 저장하되 그 값을 변경하거나 읽을 때마다 로직을 실행하는 프로퍼티를 만들어 보겠습니다.
class User(val name: String) {
var address: String = "unspecified"
set(value: String) {
println("""
Address was changed for $name:
"$field" -> "$value".""".trimIndent())
field = value
}
}
fun main(args: Array<String>) {
val user = User("Alice")
user.address = "Elsenheimerstrasse 47, 80687 Muenchen"
}
위 코드를 보면 프로퍼티의 값을 바꿀 때 필드 설정 구문을 사용합니다. (위에서는 user.address = "Elsenheimerstrasse 47, 80687 Muenchen") 이 구문은 내부적으로는 위의 세커(set)을 호출 합니다.
그리고 위에서 보면 field라는 식별자가 있는데, 위에서 부터 계속 말해온 그 필드 입니다. 그리고 이 필드는 게터에서는 읽을 수 만있고, 세터에서는 읽고 쓸 수 있습니다. (위에서는 field는 처음에는 초기화된 unspecified이가 되고 그 다음에
Elsenheimerstrasse 47, 80687 Muenchen)이 됩니다.
그래서 위의 코드를 실행 해 보면
Address was changed for Alice:
"unspecified" -> "Elsenheimerstrasse 47, 80687 Muenchen".
이런 결과가 나옵니다.
만일 main을 다음과 같이 수정 후 실행하면
fun main(args: Array<String>) {
val user = User("Alice")
user.address = "Elsenheimerstrasse 47, 80687 Muenchen"
user.address = "Muenchen 80687, 47 Elsenheimerstrasse"
}
///결과
Address was changed for Alice:
"unspecified" -> "Elsenheimerstrasse 47, 80687 Muenchen".
Address was changed for Alice:
"Elsenheimerstrasse 47, 80687 Muenchen" -> "Muenchen 80687, 47 Elsenheimerstrasse".
이렇게 됩니다.
접근자의 가시성 변경
가기성이란 public private protected 등을 말한다. 이런 가시성을 get이나 set에 추가하여 프로퍼티 접근자의 가시성을 변경할 수 있습니다.
class LengthCounter {
var counter: Int = 0
private set
fun addWord(word: String) {
counter += word.length
}
}
fun main(args: Array<String>) {
val lengthCounter = LengthCounter()
lengthCounter.addWord("Hi!")
println(lengthCounter.counter)
}
위의 코드를 보면 counter 아레 세커에 private가 선어되어있습니다. 즉, 이 counter는 LengthCounter 안에서만 수정할 수 있고 이 class 밖에서는 수정할 수 없다는 뜻입니다.
만일 코드에 lengthCounter.counter = "Hello".length 를 하면 error가 나고 setter is pirvate 라는 문구를 볼 수 있습니다.
위의 코드가 error 없이 동작하려면 private set에서 private 를 지우면 됩니다.
'2023년 이전 > kotlin' 카테고리의 다른 글
kotlin - object 키워드 (0) | 2020.02.25 |
---|---|
kotlin - 데이터 클래스와 클래스 위임 (0) | 2020.02.25 |
kotlin - 생성자 (0) | 2020.02.21 |
kotlin - 인터페이스, open, final, abstract, 가시성 변경자, 내부 클래스와 중첩 클래스, 봉인 클래스 (0) | 2020.02.21 |
kotlin - 로컬 함수와 확장 (0) | 2020.02.20 |