이 글은 Kotlin In Action을 참고 하였습니다.
더욱 자세한 설명이나 예제는 직접 책을 구매하여 확인 하시기 바랍니다
이 글은 Kotlin In Action을 참고 하였습니다.
더욱 자세한 설명이나 예제는 직접 책을 구매하여 확인 하시기 바랍니다
4.2 Class의 생성자와 property
코틀린에는 주 생성자와(primary constructor)와 부생성자가(secondary constructor)가 존재합니다.
주 생성자는 class 선언과 함께 선언하고, 부 생성자는 추가적인 생성자가 필요할때 사용합니다.
4.2.1 주 생성자와 초기화(init)
class User(val nickname: String)
주 생성자는 클래스 선언과 함께 정의됩니다.
class User constructor(_nickname: String) {
val nickname: String
init {
nickname = _nickname
}
}
constructor는 주 생성자를 나타내기 위한 키워드이나 한정자나, 다른 키워드가 붙지 않는다면 생략하는것도 가능합니다.
또한 init{..}은 초기화 작업을 할때 사용합니다.
주로 클래스가 객체로 생성될때 초기화 하는 작업이 들어갑니다.
class User(_nickname: String) {
val nickname = _nickname
}
자바에서 하는것처럼 이렇게 사용해도 무방합니다.
class User(val nickname: String,
val isSubscribed: Boolean = true)
fun main(args: Array) {
val alice = User("Alice")
println(alice.isSubscribed)
val bob = User("Bob", false)
println(bob.isSubscribed)
val carol = User("Carol", isSubscribed = false)
println(carol.isSubscribed)
}
주 생성자에 default값을 지정해서 사용해도 됩니다.
만약 모든 생성자에 default값을 넣는다면, 컴파일러는 기본적으로 아무것도 없는 생성자를 생성합니다.
그리고 나서 기본값을 대입하는 작업을 합니다.
만약 상속을 통해서 class를 만드는경우 부모의 생성자가 있다면 자식 클래스에서 반드시 호출해야 합니다.
open class Person(val age: Int) {...}
class Batman(val age: Int): Person(int) {
...
}
생성자를 정의하지 않으면 기본으로 인자가 없는 생성자를 만들어줍니다.(자바랑 동일한 동작을 합니다.)
따라서 상속을 받을때는 부모클래스를 명시하고 ()붙여야 합니다.
기본 생성자를 호출하겠단 의미 입니다.
반대로 interface는 생성자가 없으니 ()를 붙일수 없습니다.
()가 있고 없고에 따라서 클래스의 상속인지, 인터페이스의 implements인지를 구분 할 수 있습니다.
생성자를 외부에 노출하지 않으려면 private을 이용하여 생성자를 생성합니다.
class Superman private constructor() {...}
다만 이렇게 하면 클래스 내부에서만 생성자에 접근할수 있는데, 어디서 많이 보던 패턴입니다.
코틀린에서 singleton을 만들때 생성자를 외부에 노출하지 않는 방법이며, 싱글톤을 완벽하게 구현하려면 object을 이용해야하는데, 이는 다음번에 다루겠습니다.
4.2.2 부 생성자(secondary constructor)
class TextView: View {
constructor(context: Context) : this(context, null) {
....
}
constructor(context: Context, attr: AttributeSet) : super(cotext, attr) {
....
}
이때 상속받은 부모의 생성자는 꼭 호출해야 합니다.
부모의 생성자는 super(), 내 생성자중 다른 생성자는 this()로 호출이 가능합니다.
클래스의 주 생성자가 없다면, 모든 부 생성자들은 상위 클래스를 초과 하거나, 다른생성자에게 이를 위임해야 합니다.
4.2.3 인터페이스에 property 추가
fun getFacebookName(accountId: Int) = "fb:$accountId"
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) {
println(PrivateUser("test@kotlinlang.org").nickname)
println(SubscribingUser("test@kotlinlang.org").nickname)
}
User는 nickname이란 property를 가지고 있습니다.
- PrivateUser는 주 생성자에서 override 메서드를 이용해서 부모의 property값을 처리 합니다.
- SubscribingUser의 경우 class 내부에서 명시적으로 nickname을 override하고 custom getter를 제공합니다.
- FacebookUser는 함수를 이용하여 nickname값을 설정 합니다.
여기서 2번과 3번은 살짝 뉘앙스가 다릅니다.
SubscribingUser는 getter를 호출할 때 마다 값을 계산해서 반환합니다.
반면, FacebookUser는 객체 생성지 한번만 값을 구해서 backing field에 설정해 놓고 호출때마다 저장된 field을 반환합니다.
4.2.4 Backing Field
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) {
val user = User("Alice")
user.address = "Elsenheimerstrasse 47, 80687 Muenchen"
}
getter에서는 field값을 읽기만 가능하고, setter에서는 읽고 쓰기가 가능합니다.
4.2.5 get과 set의 접근자(한정자) 제어
class LengthCounter {
var counter: Int = 0
private set
fun addWord(word: String) {
counter += word.length
}
}
fun main(args: Array) {
val lengthCounter = LengthCounter()
lengthCounter.addWord("Hi!")
println(lengthCounter.counter)
}
위 코드에서 counter의 set을 private을 한정했습니다.
따라서 counter property는 외부에서 접근할 수가 없습니다.
'개발이야기 > Kotlin' 카테고리의 다른 글
[Kotlin] 코틀린 object (6) | 2018.04.23 |
---|---|
[kotlin] 코틀린 데이터 클래스와 위임 (0) | 2018.04.22 |
[Kotlin] 코틀린 삼중따옴표, 정규식, 문자열, 중첩함수 , 확장함수 (2) | 2018.04.18 |
[Kotlin] 코틀린 확장함수와 프로퍼티, 가변인자, 중위함수 (0) | 2018.04.16 |
[Kotlin] 코틀린의 Collection (2) | 2018.04.15 |