Свойства классов

Свойства классов #

Васильев Андрей Михайлович, 2024

Версии презентации


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

Свойства в классах Kotlin могут быть объявлены либо как изменяемые (mutable) и неизменяемые (read-only) — 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
}

Работа ключевого слова val #

Объявление поля с помощью ключевого слова val

class Sample(val name: String)

можно сравнить со следующим кодом на Java

public class Sample {
    private String name;
    public Sample(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

Работа ключевого слова var #

Объявление поля с помощью ключевого слова var

class Counter(var value: Int)

можно сравнить со следующим кодом на Java

public class Counter {
    private int value;
    public Counter(int value) {
        this.value = value;
    }
    public int getValue() {
        return value;
    }
    public void setValue(int value) {
        this.value = value
    }
}

Только свойства в Kotlin можно использовать в левой части присваивания


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

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

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

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

var initialized = 1 // имеет тип Int, стандартный геттер и сеттер
var allByDefault    // ошибка: необходима явная инициализация, 
                    // предусмотрены стандартные геттер и сеттер

Неизменяемые свойства #

Синтаксис объявления неизменяемых свойств имеет два отличия от синтаксиса объявления изменяемых свойств:

  • объявление неизменяемого свойства начинается с ключевого слова val вместо var
  • объявление сеттера запрещено
val simple: Int? // имеет тип Int, стандартный геттер, 
                 // должен быть инициализирован в конструкторе
val inferredType = 1 // имеет тип Int и стандартный геттер

Определение геттера #

Вы можете самостоятельно определить методы доступа для свойства

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

class Rectangle(val width: Int, val height: Int) {
    val area: Int
        get() = this.width * this.height // тип свойства необязателен, поскольку он может быть выведен из возвращаемого типа геттера
}

Вы можете опустить тип свойства, если его можно определить с помощью геттера.

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

Определение сеттера #

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

var stringRepresentation: String
    get() = this.toString()
    set(value) {
        setDataFromString(value) // парсит строку и устанавливает 
                                 // значения для других свойств
    }

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


Теневые поля #

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

var counter = 0 // инициализатор назначает резервное поле напрямую
    set(value) {
        if (value >= 0)
            field = value // значение при инициализации записывается 
                          // прямиком в backing field
    }

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

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

© A. M. Васильев, 2024, CC BY-SA 4.0, andrey@crafted.su