프로퍼티 = 필드 + 접근자

  • 클래스 내부의 변수 선언은 자바에서는 필드 선언을 의미하지만 코틀린에서는 프로퍼티 선언을 의미한다. 
  • 즉, 필드 뿐만 아니라 접근자 메서드도 알아서 생성해준다.

```kt

class Person(

    val name: String,       // val은 읽기 전용 프로퍼티. private 필드와 getter.

    var isMarried: Boolean  // private 필드와 getter, setter.

)

 

/* 코틀린에서 호출할 때 (자동으로 getter, setter 호출로 변환해준다) */

person.name

person.isMarried = true

 

/* 자바에서 호출할 때 */

person.getName()

person.isMarried()    // boolean 프로퍼티 getter는 is prefix다

person.setMarried(true)

```

* 반대로 자바로 작성한 클래스도 프로퍼티 명명 형식을 맞춰 준다면  코틀린 클래스 프로퍼티처럼 사용할 수 있다.

 

커스텀 접근자

단순히 값을 반환하는 접근자가 아니라 특정 연산을 수행한 결과를 반환해야 하는 경우라면, 접근자를 직접 작성할 수 있다.

```kt

val  isSquare: Boolean

    get() = height == width

 

var counter: Int = 0

    private set

```

 

isSquare 예제 처럼 호출될 때 마다 계산해서 수행 결과를 반환하는 방법이 있고,
초기화시, 그리고 종속 변수 값이 변경될 때 마다 변수 값을 다시 계산해두고 호출될 때는 단순히 계산 결과를 반환하기만 하도록 구성하는 방법이 있는데 
전자는 호출 오버헤드가, 후자는 메모리 오버헤드가 발생한다. 특별히 많이 호출되지 않는다면 전자로 짜는게 더 보기 좋은 듯.
Note ) ``kt lateinit``은 커스텀 접근자를 사용할 수 없으니 주의!

 

setter 내에서 변수 값 접근은 field 키워드로

```kt

class User (val name: String){

    var address: String = "unspecified"

        set(value: String) {

            println("Address was changed for $name: $field -> $value")

            field = value

        }

}

```

* 커스텀 접근자를 지정할 때 내부에서 ``kt field`` 키워드를 사용하지 않는다면 뒷받침 필드가 필요 없다는 의미 이므로 알아서 필드가 생성되지 않는다 (매 호출 마다 eval 된다)

 

data class에서 custom getter 사용하고 싶을 때

backing property를 사용한다.

```kt

data class Test(private val _value: Int) {

    val value

        get() = value

}

```

 

지연 초기화

  • 안드로이드에서 액티비티 초기화에 사용하는 ``kt onCreate``같이 생성자 안에서 초기화하지 않고 특별한 메소드에서 초기화해야 하는 경우 등, 지연 초기화 하고 싶은 경우 일단 변수에 ``kt null``을 집어넣고 이후에 초기화해야 하는 방식이 떠오른다.
  • 그러나 변수에 일단 ``kt null``을 집어넣으려면 타입을 ``kt Type?``로 선언해주어야 하는데, 이러면 이 변수를 가져다 쓰는 모든 구간에서 널체크를 해줘야 하므로, 굉장히 비효율적이다.
  • 지연 초기화 하면서도, 타입을 non-nullable로 사용하는 방법은?

 

backing property를 사용한 lateinit

```kt

class Person {

    private var _emails: List<Email>? = null

    val emails: List<Email>

        get() {

            if (_emails == null) {

                _emails = loadEmails(this)

            }

            return _emails!!    // var 이라 스마트 캐스팅이 작동하지 않으므로 !!

        }

}

```

  • 가능한 방법이지만, 지저분하고, thread safe 하지도 않다.

 

val 지연 초기화 : by lazy

```kt

class Person {

   val emails by lazy { loadEmails(this) }

```

  • 부대 코드가 없어 깔끔하게 끝난다.
  • lazy 함수는 기본적으로 thread safe하다.
    • 하지만 필요에 따라 동기화에 사용할 락을 lazy에 전달 할 수도 있고, 반대로 lazy 함수가 동기화를 하지 못하게 막을 수도 있다.
  • by를 이용해서 프로퍼티 위임한 것인데, 상세 내용은 [Kotlin] delegate 키워드 : by

 

var 지연 초기화 : lateinit

```kt

class LateInitTest {

    lateinit var myService: MyService

 

    @Before fun setUp() {

        myService = MyService()

    }

}

 

// 초기화 되었는지 체크는

::myService.isInitialized

```

  • 초기화하기 전에 프로퍼티에 접근하면 예외가 발생하며, 단순 NPE가 발생하는 것 보다 확인하기 용이하다.
  • Note ) ``kt lateinit`` modifier is not allowed on properties of primitive types

 

lateinit VS Delegates.notNull()

codechacha.com/ko/diff-between-deligate-and-lateinit-in-kotlin/  

 

lateinit은 Primitive type에는 사용할 수 없다.

 

'Java Stack > Kotlin' 카테고리의 다른 글

[Kotlin] delegate 키워드 : by  (0) 2017.12.02
[Kotlin/Java] data class  (0) 2017.12.02
[Kotlin] Class, 생성자  (0) 2017.12.01
[Kotlin] enum / when / sealed  (0) 2017.12.01
[Kotlin/Java] Inner Class / Nested Class  (0) 2017.12.01