[Kotlin] 3-2. 프로퍼티와 필드

프로퍼티와 필드

프로퍼티 선언

코틀린에서 클래스는 프로퍼티를 가질 수 있다
var키워드로 변경 가능하게 선언
val키워드로 읽기 전용 선언

class Address {
  var name: String = ...
  var street: String = ...
  var state: String? = ...
}

자바 필드처럼 이름으로 프로퍼티 참조 가능

fun copyAddress(address: Address): Address {
  val result = Address()  // 코틀린에 'new'키워드 없음
  result.name = address.name  // 접근자 실행
  result.street = address.street
  // ...
  return result
}

Getters와 Setters

프로퍼티 선언의 전체 구문은 다음과 같다

var <propertyName>[: <PropertyType>] [= <property_initializer>]
  [<getter>]
  [<setter>]

initializer, getter, setter는 선택사항 / 프로퍼티 타입은 initializer의 리턴타입에서 유추할 수 있으면 생략 가능

예제:

var allByDefault: Int?  // 에러(initializer-초기화) 필요, 기본 getter와 setter 적용
var initialized = 1  // Int타입을 갖고, 기본 getter와 setter

읽기 전용 프로퍼티 선언은 변경가능 프로퍼티 선언과 두 가지가 다르다
var대신에 val로 시작 / setter를 허용하지 않는다

val simple: Int?  // Int타입이고, 기본 getter, 생성자에서 초기화해야 함
val inferredType = 1  // Int타입이고, 기본 getter

일반 함수처럼 프로퍼티 선언에 커스텀 접근자를 작성할 수 있다 / 다음은 커스텀 getter의 예

val isEmpty: Boolean
  get() = this.size == 0

커스텀 setter

var stringRepresentation: String
  get() = this.toString()
  set(value) {
    setDataFromString(value)  // 문자열을 파싱해서 다른 프로퍼티에 값을 할당
  }

setter파라미터의 이름을 value로 하는것이 관례인데, 선호하는 다른 이름 가능
getter에서 타입을 추론할 수 있으면 프로퍼티 타입을 생략할 수 있다

val isEmpty get() = this.size == 0  // Boolean 타입

프로퍼티의 기본 구현을 바꾸지 않고 애노테이션을 붙이거나 접근자의 가시성을 바꾸고 싶다면 몸체 없는 접근자 정의

var setterVisibility: String = "abc"
  private set  // setter를 private로 하고 기본 구현을 가짐

var setterWithAnnotation: Any? = null
  @Inject set  // setter에 @Inject애노테이션 사용

지원 필드

코틀린 클래스는 필드를 가질 수 없다 / 때떄로 커스텀 접근자를 사용할 때 지원 필드가 필요
이런 목적으로 코틀린은 field식별자로 접근할 수 있는 지원 필드를 자동으로 제공

var counter = 0  // 초기값을 지원 필드에 직접 쓴다
  set(value) {
    if(value >= 0) field = value
  }

field식별자는 오직 프로퍼티의 접근자에서만 사용 가능
접근자의 기본 구현을 적어도 한 개 이상 사용하거나 또는 커스텀 접근자에서 field식별자로 접근할 경우,
프로퍼티를 위한 지원 필드 생성
다음과 같은 경우는 지원 필드가 없다

val isEmpty: Boolean
  get() = this.size == 0

컴파일 타임 상수

컴파일 시점에 알아야 할 프로퍼티 값을 const수식어를 이용해서 컴파일 타임 상수로 표시할 수 있다
그런 프로퍼티는 다음 조건을 충족해야함

이런 프로퍼티는 애노테이션에서 사용할 수 있다

const val SUBSYSTEM_DEPRECATED: String = "this subsystem is deprecated"
@Deprecated(SUBSYSTEM_DEPRECATED) fun foo { ... }