본문으로 건너뛰기

속성

프로퍼티 선언

Kotlin 클래스에서 프로퍼티는 var 키워드를 사용하여 변경 가능하게 선언하거나 val 키워드를 사용하여 읽기 전용으로 선언할 수 있습니다.

class Address {
var name: String = "Holmes, Sherlock"
var street: String = "Baker"
var city: String = "London"
var state: String? = null
var zip: String = "123456"
}

프로퍼티를 사용하려면 간단히 해당 이름으로 참조하면 됩니다:

fun copyAddress(address: Address): Address {
val result = Address() // Kotlin에는 'new' 키워드가 없습니다.
result.name = address.name // 접근자가 호출됩니다.
result.street = address.street
// ...
return result
}

Getter와 Setter

프로퍼티를 선언하는 전체 구문은 다음과 같습니다:

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

초기화, getter 및 setter는 선택 사항입니다. 프로퍼티 타입은 초기화 또는 getter의 반환 타입에서 추론할 수 있는 경우 선택 사항입니다. 아래에 표시된 대로:

var initialized = 1 // 타입은 Int이며, 기본 getter 및 setter를 가집니다.
// var allByDefault // ERROR: 명시적 초기화가 필요하며, 기본 getter 및 setter가 암시됩니다.

읽기 전용 프로퍼티 선언의 전체 구문은 변경 가능한 프로퍼티와 두 가지 방식으로 다릅니다: var 대신 val로 시작하고 setter를 허용하지 않습니다:

val simple: Int? // 타입은 Int이며, 기본 getter를 가지며, 생성자에서 초기화해야 합니다.
val inferredType = 1 // 타입은 Int이며, 기본 getter를 가집니다.

프로퍼티에 대한 사용자 정의 접근자를 정의할 수 있습니다. 사용자 정의 getter를 정의하면 프로퍼티에 접근할 때마다 호출됩니다 (이러한 방식으로 계산된 프로퍼티를 구현할 수 있습니다). 다음은 사용자 정의 getter의 예입니다:


class Rectangle(val width: Int, val height: Int) {
val area: Int // getter의 반환 타입에서 추론할 수 있으므로 프로퍼티 타입은 선택 사항입니다.
get() = this.width * this.height
}

fun main() {
val rectangle = Rectangle(3, 4)
println("Width=${rectangle.width}, height=${rectangle.height}, area=${rectangle.area}")
}

getter에서 타입을 추론할 수 있는 경우 프로퍼티 타입을 생략할 수 있습니다:

val area get() = this.width * this.height

사용자 정의 setter를 정의하면 초기화를 제외하고 프로퍼티에 값을 할당할 때마다 호출됩니다. 사용자 정의 setter는 다음과 같습니다:

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

관례에 따라 setter 매개변수의 이름은 value이지만, 원하는 경우 다른 이름을 선택할 수 있습니다.

접근자에 어노테이션을 달거나 가시성을 변경해야 하지만 기본 구현을 변경하고 싶지 않은 경우, 본문을 정의하지 않고 접근자를 정의할 수 있습니다:

var setterVisibility: String = "abc"
private set // setter는 private이며 기본 구현을 가집니다.

var setterWithAnnotation: Any? = null
@Inject set // setter에 Inject 어노테이션을 답니다.

Backing Fields (뒷받침 필드)

Kotlin에서 필드는 메모리에서 값을 보유하기 위해 프로퍼티의 일부로만 사용됩니다. 필드는 직접 선언할 수 없습니다. 그러나 프로퍼티에 뒷받침 필드가 필요한 경우 Kotlin은 자동으로 제공합니다. 이 뒷받침 필드는 field 식별자를 사용하여 접근자에서 참조할 수 있습니다:

var counter = 0 // 초기화 프로그램은 뒷받침 필드를 직접 할당합니다.
set(value) {
if (value >= 0)
field = value
// counter = value // ERROR StackOverflow: 실제 이름 'counter'를 사용하면 setter가 재귀적이 됩니다.
}

field 식별자는 프로퍼티의 접근자에서만 사용할 수 있습니다.

뒷받침 필드는 하나 이상의 접근자의 기본 구현을 사용하거나, 사용자 정의 접근자가 field 식별자를 통해 참조하는 경우 프로퍼티에 대해 생성됩니다.

예를 들어, 다음 경우에는 뒷받침 필드가 없습니다:

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

Backing Properties (뒷받침 프로퍼티)

이러한 암시적 뒷받침 필드 체계에 맞지 않는 작업을 수행하려는 경우, 언제든지 _뒷받침 프로퍼티_를 사용할 수 있습니다:

private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
get() {
if (_table == null) {
_table = HashMap() // 타입 매개변수가 추론됩니다.
}
return _table ?: throw AssertionError("Set to null by another thread")
}
노트

JVM에서: 기본 getter 및 setter가 있는 private 프로퍼티에 대한 접근은 함수 호출 오버헤드를 피하기 위해 최적화됩니다.

Compile-time Constants (컴파일 시간 상수)

읽기 전용 프로퍼티의 값을 컴파일 시간에 알 수 있는 경우 const 수정자를 사용하여 _컴파일 시간 상수_로 표시합니다. 이러한 프로퍼티는 다음 요구 사항을 충족해야 합니다:

  • 최상위 프로퍼티이거나 object 선언 또는 _컴패니언 객체_의 멤버여야 합니다.
  • String 타입 또는 primitive 타입의 값으로 초기화해야 합니다.
  • 사용자 정의 getter일 수 없습니다.

컴파일러는 상수의 사용을 인라인하여 상수 참조를 실제 값으로 대체합니다. 그러나 필드는 제거되지 않으므로 리플렉션을 사용하여 상호 작용할 수 있습니다.

이러한 프로퍼티는 어노테이션에도 사용할 수 있습니다:

const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated"

@Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { ... }

Late-initialized Properties and Variables (늦게 초기화되는 프로퍼티 및 변수)

일반적으로 널이 될 수 없는 타입으로 선언된 프로퍼티는 생성자에서 초기화해야 합니다. 그러나 그렇게 하는 것이 편리하지 않은 경우가 많습니다. 예를 들어, 프로퍼티는 종속성 주입을 통해 초기화하거나 단위 테스트의 설정 메서드에서 초기화할 수 있습니다. 이러한 경우 생성자에서 널이 될 수 없는 초기화 프로그램을 제공할 수 없지만, 클래스 본문 내에서 프로퍼티를 참조할 때 null 검사를 피하고 싶을 수 있습니다.

이러한 경우를 처리하기 위해 lateinit 수정자로 프로퍼티를 표시할 수 있습니다:

public class MyTest {
lateinit var subject: TestSubject

@SetUp fun setup() {
subject = TestSubject()
}

@Test fun test() {
subject.method() // 직접 역참조
}
}

이 수정자는 클래스 본문 내에서 선언된 var 프로퍼티 (기본 생성자가 아닌 경우, 그리고 프로퍼티에 사용자 정의 getter 또는 setter가 없는 경우)와 최상위 프로퍼티 및 지역 변수에 사용할 수 있습니다. 프로퍼티 또는 변수의 타입은 널이 될 수 없어야 하며, primitive 타입이 아니어야 합니다.

초기화되기 전에 lateinit 프로퍼티에 접근하면 접근되는 프로퍼티와 초기화되지 않았다는 사실을 명확하게 식별하는 특수 예외가 발생합니다.

Checking whether a lateinit var is initialized (lateinit var가 초기화되었는지 확인)

lateinit var가 이미 초기화되었는지 확인하려면 해당 프로퍼티에 대한 참조에서 .isInitialized를 사용하십시오:

if (foo::bar.isInitialized) {
println(foo.bar)
}

이 검사는 동일한 타입, 외부 타입 중 하나 또는 동일한 파일의 최상위 레벨에서 선언될 때 어휘적으로 접근할 수 있는 프로퍼티에만 사용할 수 있습니다.

Overriding Properties (프로퍼티 오버라이딩)

프로퍼티 오버라이딩을 참조하십시오.

Delegated Properties (위임된 프로퍼티)

가장 일반적인 종류의 프로퍼티는 단순히 뒷받침 필드에서 읽거나 (쓰기) 쓰지만, 사용자 정의 getter 및 setter를 사용하면 프로퍼티를 사용하여 프로퍼티의 모든 종류의 동작을 구현할 수 있습니다. 첫 번째 종류의 단순성과 두 번째 종류의 다양성 사이 어딘가에, 프로퍼티가 할 수 있는 일에 대한 공통 패턴이 있습니다. 몇 가지 예: lazy 값, 주어진 키로 맵에서 읽기, 데이터베이스에 접근, 접근 시 리스너에 알림.

이러한 일반적인 동작은 위임된 프로퍼티를 사용하여 라이브러리로 구현할 수 있습니다.