Kotlin: как сделать публичный геттер, но приватный сеттер для свойства?
Решение: переопределение сеттера с модификатором private
В Kotlin для создания публичного геттера и приватного сеттера нужно использовать var (изменяемое свойство) и явно переопределить сеттер, добавив перед ним модификатор private. Геттер при этом остаётся публичным по умолчанию.
class User {
var name: String = ""
private set // приватный сеттер, геттер остаётся публичным
}
Теперь свойство name можно читать из любого места, но изменять — только внутри класса:
fun main() {
val user = User()
// Чтение работает:
println(user.name) // Выведет пустую строку
// Изменение извне — ошибка компиляции:
// user.name = "Alice" // Ошибка: Cannot assign to 'name': the setter is private
// Изменение внутри класса — работает:
user.updateName("Alice")
println(user.name) // Выведет "Alice"
}
class User {
var name: String = ""
private set
fun updateName(newName: String) {
name = newName // Доступ к приватному сеттеру разрешён
}
}
Как это работает
В Kotlin каждое свойство (var или val) автоматически имеет геттер и (для var) сеттер. По умолчанию оба они имеют ту же видимость, что и само свойство. Если вы пишете:
var name: String = ""
То и геттер, и сеттер — публичные. Добавив private set, вы переопределяете только сеттер, делая его приватным. Геттер остаётся публичным, так как вы его не трогали.
Важные нюансы
- Модификатор ставится перед
set—private set,protected set,internal set. - Если свойство объявлено как
val— сеттера нет вообще, только геттер. Для изменяемого свойства с приватным сеттером используйтеvar. - Инициализация обязательна — свойство должно быть инициализировано (в теле класса или через конструктор), иначе компилятор выдаст ошибку. Исключение: если вы используете
lateinit var. - В конструкторе — если свойство объявлено в первичном конструкторе, модификатор доступа для сеттера указывается так же:
class User(private var _name: String) {
var name: String
get() = _name
private set(value) {
_name = value
}
}
Но это избыточно — обычно достаточно простой формы var name: String = ""; private set.
Когда это полезно
- Инкапсуляция: вы хотите контролировать изменение свойства через методы (например, с валидацией).
- Свойства, которые изменяются только внутри класса или в наследниках (используйте
protected set). - Счётчики, флаги, состояния — чтобы внешний код мог читать, но не менять их напрямую.
Таким образом, решение очень простое: объявите var и добавьте private set. Никаких дополнительных классов или вспомогательных полей не нужно — Kotlin делает это элегантно.
Альтернативный подход: использование backing field с явным геттером и сеттером
Если вам нужно больше контроля (например, валидация при установке значения или кастомная логика в геттере), можно явно объявить backing field с помощью поля field и написать полные реализации геттера и сеттера. При этом сеттер можно сделать приватным, а геттер — публичным.
class User {
private var _name: String = ""
var name: String
get() = _name
private set(value) {
// Пример валидации
if (value.isNotBlank()) {
_name = value
} else {
println("Имя не может быть пустым")
}
}
fun updateName(newName: String) {
name = newName // Использует приватный сеттер
}
}
Этот подход даёт те же возможности, что и private set, но позволяет:
- Добавить проверки в сеттер (валидация, логирование).
- Использовать разные имена для backing field и публичного свойства (хотя это необязательно).
- Сделать геттер тоже кастомным (например, возвращать форматированное значение).
Однако, если вам не нужна дополнительная логика, проще и короче использовать стандартный синтаксис private set — это идиоматичный способ в Kotlin. Явный backing field оправдан только тогда, когда без него не обойтись.
Ответов пока нет.
Войдите чтобы оставить ответ