Get set variable kotlin

Свойства

Свойства в классах Kotlin могут быть объявлены либо как изменяемые (mutable) и неизменяемые (read-only) — var и val соответственно.

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

fun copyAddress(address: Address): Address < val result = Address() // в Kotlin нет никакого слова `new` result.name = address.name // вызов методов доступа result.street = address.street // . return result >

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

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

Инициализатор 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 , но вы можете использовать любое другое.

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

var setterVisibility: String = "abc" private set // сеттер имеет private доступ и стандартную реализацию var setterWithAnnotation: Any? = null @Inject set // аннотирование сеттера с помощью Inject 

Теневые поля

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

var counter = 0 // инициализатор назначает резервное поле напрямую set(value) < if (value >= 0) field = value // значение при инициализации записывается // прямиком в backing field // counter = value // ERROR StackOverflow: Использование 'counter' сделало бы сеттер рекурсивным > 

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

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

Например, в примере ниже не будет никакого теневого поля:

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

Теневые свойства

Если вы хотите предпринять что-то такое, что выходит за рамки вышеуказанной схемы неявного теневого поля, вы всегда можете использовать теневое свойство (backing property).

private var _table: Map? = null public val table: Map get() < if (_table == null) < _table = HashMap() // параметры типа вычисляются автоматически // (ориг.: "Type parameters are inferred") >return _table ?: throw AssertionError("Set to null by another thread") > 

On the JVM: Access to private properties with default getters and setters is optimized to avoid function call overhead. —>

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

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

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

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

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

const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated" @Deprecated(SUBSYSTEM_DEPRECATED) fun foo()

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

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

Для того чтобы справиться с такой задачей, вы можете пометить свойство модификатором lateinit .

public class MyTest < lateinit var subject: TestSubject @SetUp fun setup() < subject = TestSubject() >@Test fun test() < subject.method() // объект инициализирован, проверять на null не нужно >> 

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

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

Проверка инициализации lateinit var

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

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

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

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

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

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

© 2015—2023 Open Source Community

Источник

Get set variable kotlin

Геттеры (getter) и сеттеры (setter) (еще их называют методами доступа) позволяют управлять доступом к переменной. Их формальный синтаксис:

var имя_свойства[: тип_свойства] [= инициализатор_свойства] [getter] [setter]

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

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

Сеттер

Сеттер определяет логику установки значения переменной. Он определяется с помощью слова set . Например, у нас есть переменная age , которая хранит возраст пользователя и представляет числовое значение.

Но теоретически мы можем установить любой возраст: 2, 6, -200, 100500. И не все эти значения будут корректными. Например, у человека не может быть отрицательного возраста. И для проверки входных значений можно использовать сеттер:

var age: Int = 18 set(value)< if((value>0) and (value <110)) field = value >fun main() < println(age) // 18 age = 45 println(age) // 45 age = -345 println(age) // 45 >

Блок set определяется сразу после свойства, к которому оно относится — в данном случае после свойства age . При этом блок set фактически представляет собой функцию, которая принимает один параметр — value, через этот параметр передается устанавливаемое значение. Например, в выражении age = 45 число 45 и будет представлять тот объект, который будет храниться в value.

В блоке set проверяем, входит ли устанавливаемое значение в диапазон допустимых значений. Если входит, то есть если значение корректно, то передаем его объекту field . Если значение некорректно, то свойство просто сохраняет свое предыдущее значение.

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

В функции main при втором обращении к сеттеру ( age = -345 ) можно заметить, что значение свойства age не изменилось. Так как новое значение -345 не входит в диапазон от 0 до 110.

геттер

Геттер управляет получением значения свойства и определяется с помощью ключевого слова get :

var age: Int = 18 set(value)< if((value>0) and (value <110)) field = value >get() = field

Справа от выражения get() через знак равно указывается возвращаемое значение. В данном случае возвращается значения поля field , которое хранит значение свойства name. Хотя в таком геттер большого смысла нет, поскольку получить подобное значение мы можем и без геттера.

Если геттер должен содержать больше инструкций, то геттер можно оформить в блок с кодом внутри фигурных скобок:

var age: Int = 18 set(value)< println("Call setter") if((value>0) and (value <110)) field = value >get()

Если геттер оформлен в блок кода, то для возвращения значения необходимо использовать оператор return . И, таким образом, каждый раз, когда мы будем получать значение переменной age (например, в случае с вызовом println(age) ), будет срабатывать геттер, когда возвращает значение. Например:

Консольный вывод программы:

Call getter 18 Call setter Call getter 45

Использование геттеров и сеттеров в классах

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

fun main() < val bob: Person = Person("Bob") bob.age = 25 // вызываем сеттер println(bob.age) // 25 bob.age = -8 // вызываем сеттер println(bob.age) // 25 >class Person(val name: String)< var age: Int = 1 set(value)< if((value>0) and (value <110)) field = value >>

При втором обращении к сеттеру ( bob.age = -8 ) можно заметить, что значение свойства age не изменилось. Так как новое значение -8 не входит в диапазон от 0 до 110.

Вычисляемый геттер

Геттер может возвращать вычисляемые значения, которые могут задействовать несколько свойств:

fun main() < val tom = Person("Tom", "Smith") println(tom.fullname) // Tom Smith tom.lastname = "Simpson" println(tom.fullname) // Tom Simpson >class Person(var firstname: String, var lastname: String)

Здесь свойство fullname определяет блок get, который возвращает полное имя пользователя, созданное на основе его свойств firstname и lastname. При этом значение самого свойства fullname напрямую мы изменить не можем — оно определено доступно только для чтения. Однако если изменятся значения составляющих его свойств — firstname и lastname, то также изменится значение, возвращаемое из fullname.

Использование полей для хранения значений

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

Можно использовать одновременно и геттер, и сеттер:

fun main() < val tom = Person("Tom") println(tom.age) // 1 tom.age = 37 println(tom.age) // 37 tom.age = 156 println(tom.age) // 37 >class Person(val name: String) < private var _age = 1 var age: Int set(value)< if((value >0) and (value < 110)) _age = value >get()= _age >

Здесь для свойства age добавлены геттер и сеттер, которые фактически являются надстройкой над полей _age , которое собственно хранит значение.

Источник

Читайте также:  Про мышей
Оцените статью