Spec-Zone.ru › Kotlin 1.4

Свойства и поля

Объявление свойств

Свойства в классах 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() // there's no 'new' keyword in Kotlin
    result.name = address.name // accessors are called
    result.street = address.street
    // ...
    return result
}

Геттеры и сеттеры

Полный синтаксис объявления свойства:

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

Инициализатор, геттер и сеттер являются необязательными. Тип свойства необязателен, если его можно вывести из инициализатора (или из типа возвращаемого значения геттера, как показано ниже).

Примеры:

var allByDefault: Int? // error: explicit initializer required, default getter and setter implied
var initialized = 1 // has type Int, default getter and setter

Полный синтаксис объявления только для чтения свойства отличается от изменяемого свойства двумя способами: он начинается с val, а не с var, и не допускает сеттера:

val simple: Int? // has type Int, default getter, must be initialized in constructor
val inferredType = 1 // has type Int and a default getter

Мы можем определить пользовательские аксессоры для свойства. Если мы определим пользовательский геттер, он будет вызываться каждый раз, когда мы обратимся к свойству (это позволяет нам реализовать вычисляемое свойство). Вот пример пользовательского геттера:

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

Если мы определим пользовательский сеттер, он будет вызываться каждый раз, когда мы присваиваем значение свойству. Пользовательский сеттер выглядит так:

var stringRepresentation: String
    get() = this.toString()
    set(value) {
        setDataFromString(value) // parses the string and assigns values to other properties
    }

По соглашению, имя параметра сеттера — value, но вы можете выбрать другое имя, если хотите.

С Kotlin 1.1 вы можете опустить тип свойства, если его можно вывести из геттера:

val isEmpty get() = this.size == 0  // has type Boolean

Если вам нужно изменить видимость аксессора или добавить аннотацию к нему, но не нужно изменять реализацию по умолчанию, вы можете определить аксессор без определения его тела:

var setterVisibility: String = "abc"
    private set // the setter is private and has the default implementation

var setterWithAnnotation: Any? = null
    @Inject set // annotate the setter with Inject

Вспомогательные поля

Поля не могут быть объявлены напрямую в классах Kotlin. Однако, когда свойству необходимо вспомогательное поле, Kotlin предоставляет его автоматически. Это вспомогательное поле можно использовать в аксессорах, используя идентификатор field:

var counter = 0 // Note: the initializer assigns the backing field directly
    set(value) {
        if (value >= 0) field = value
    }

Идентификатор field может быть использован только в аксессорах свойства.

Вспомогательное поле будет сгенерировано для свойства, если оно использует реализацию по умолчанию по крайней мере для одного из аксессоров, или если пользовательский аксессор ссылается на него через идентификатор field.

Например, в следующем случае вспомогательного поля не будет:

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

Вспомогательные свойства

Если вы хотите сделать что-то, что не подходит к схеме «неявное вспомогательное поле», вы всегда можете вернуться к вспомогательному свойству:

private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
    get() {
        if (_table == null) {
            _table = HashMap() // Type parameters are inferred
        }
        return _table ?: throw AssertionError("Set to null by another thread")
    }

В JVM: Доступ к приватным свойствам с геттерами и сеттерами по умолчанию оптимизирован, так что в этом случае не вводится накладные расходы на вызов функций.

Константы времени компиляции

Если значение только для чтения свойства известно на этапе компиляции, пометьте его как константу времени компиляции, используя модификатор const. Такие свойства должны удовлетворять следующим требованиям:

  • На верхнем уровне или членом объявления object или companion object.
  • Инициализировано значением типа String или примитивного типа.
  • Нет пользовательского геттера.

Такие свойства могут использоваться в аннотациях:

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

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

Свойства и переменные с отложенной инициализацией

Обычно свойства, объявленные как имеющие не null тип, должны быть инициализированы в конструкторе. Однако это часто неудобно. Например, свойства могут быть инициализированы с помощью инъекции зависимостей или в методе настройки юнит-теста. В этом случае вы не можете предоставить не null инициализатор в конструкторе, но все равно хотите избежать проверок на null при обращении к свойству внутри тела класса.

Для этого случая вы можете пометить свойство модификатором lateinit:

public class MyTest {
    lateinit var subject: TestSubject

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

    @Test fun test() {
        subject.method()  // dereference directly
    }
}

Модификатор может быть использован для var свойств, объявленных внутри тела класса (не в первичном конструкторе, и только если у свойства нет пользовательского геттера или сеттера) и, начиная с Kotlin 1.2, для свойств и локальных переменных на верхнем уровне. Тип свойства или переменной должен быть не null, и он не должен быть примитивным типом.

Обращение к lateinit свойству до его инициализации вызывает специальное исключение, которое четко идентифицирует обращаемое свойство и тот факт, что оно не было инициализировано.

Проверка инициализации lateinit var (с 1.2)

Чтобы проверить, было ли уже инициализировано lateinit var, используйте .isInitialized для ссылки на это свойство:

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

Эта проверка доступна только для свойств, которые лексически доступны, т.е. объявлены в том же типе или в одном из внешних типов, или на верхнем уровне в том же файле.

Переопределение свойств

См. Переопределение свойств

Делегированные свойства

Наиболее распространенный тип свойств просто считывает (и, возможно, записывает) во вспомогательное поле. С другой стороны, с помощью пользовательских геттеров и сеттеров можно реализовать любое поведение свойства. Между этими крайностями есть общие шаблоны работы свойства. Несколько примеров: ленивые значения, чтение из карты по заданному ключу, доступ к базе данных, уведомление слушателя об обращении и т. д.

Такие общие действия могут быть реализованы в библиотеках с использованием делегированных свойств.

© 2010–2020 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/reference/properties.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API