Учитывая следующий класс Котлина:
data class Test(val value: Int)
Как мне переопределить getter Int
так, чтобы он возвращал 0, если значение отрицательное?
Если это невозможно, каковы некоторые методы для достижения подходящего результата?
Учитывая следующий класс Котлина:
data class Test(val value: Int)
Как мне переопределить getter Int
так, чтобы он возвращал 0, если значение отрицательное?
Если это невозможно, каковы некоторые методы для достижения подходящего результата?
Проведя почти полный год написания Kotlin ежедневно, я обнаружил, что делать это с классами данных - это плохая практика. Их 3 действительных подхода к этому, и после того, как я объясню, почему подход, предлагаемый другими людьми, является плохим.
У вашей бизнес-логики, которая создает data class
, измените значение, равное 0 или выше, перед вызовом конструктора с плохим значением. Это, вероятно, лучший подход для большинства случаев.
Не используйте data class
. Используйте обычный class
, и ваша среда IDE генерирует методы equals
и hashCode
для вас (или не нужно, если они вам не нужны). Да, вам придется повторно генерировать его, если какое-либо из свойств будет изменено на объекте, но вы останетесь с полным контролем над объектом.
class Test(value: Int) {
val value: Int = value
get() = if (field < 0) 0 else field
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Test) return false
return true
}
override fun hashCode(): Int {
return javaClass.hashCode()
}
}
Создайте дополнительное безопасное свойство для объекта, который делает то, что вы хотите, вместо того, чтобы иметь частное значение, которое эффективно задерживается.
data class Test(val value: Int) {
val safeValue: Int
get() = if (value < 0) 0 else value
}
Плохой подход, который предлагают другие ответы:
data class Test(private val _value: Int) {
val value: Int
get() = if (_value < 0) 0 else _value
}
Проблема с этим подходом заключается в том, что классы данных на самом деле не предназначены для изменения данных, подобных этому. Они действительно предназначены для хранения данных. Переопределение геттера для такого класса данных будет означать, что Test(0)
и Test(-1)
не будут equal
друг друга и будут иметь разные hashCode
s, но при вызове .value
они будут иметь одинаковый результат, Это непоследовательно, и, хотя это может сработать для вас, другие люди в вашей команде, которые видят, что это класс данных, могут случайно злоупотреблять им, не понимая, как вы его изменили/сделали его не так, как ожидалось (т.е. t правильно работать в Map
или Set
).
Вы можете попробовать что-то вроде этого:
data class Test(private val _value: Int) {
val value = _value
get(): Int {
return if (field < 0) 0 else field
}
}
assert(1 == Test(1).value)
assert(0 == Test(0).value)
assert(0 == Test(-1).value)
assert(1 == Test(1)._value) // Fail because _value is private
assert(0 == Test(0)._value) // Fail because _value is private
assert(0 == Test(-1)._value) // Fail because _value is private
В классе данных вы должны пометить параметры основного конструктора либо val
, либо var
.
Я назначаю значение _value
в value
, чтобы использовать желаемое имя для свойства.
Я определил пользовательский аксессуар для свойства с описанной вами логикой.
Ответ зависит от того, какие возможности вы используете, что обеспечивает data
. @EPadron упомянул отличный трюк (улучшенная версия):
data class Test(private val _value: Int) {
val value: Int
get() = if (_value < 0) 0 else _value
}
Это будет работать так, как ожидалось, e.i имеет поле one, один получатель, правый equals
, hashcode
и component1
. Уловка состоит в том, что toString
и copy
являются странными:
println(Test(1)) // prints: Test(_value=1)
Test(1).copy(_value = 5) // <- weird naming
Чтобы устранить проблему с помощью toString
, вы можете переопределить ее руками. Я не знаю, как исправить имя параметра, но не использовать data
вообще.
Вы можете просто
data class Test(private val number: Int) {
var value = number
get(){
return if (field < 0) 0 else field
}
}
Обратитесь к Свойствам и полям
Это, по-видимому, один (среди прочих) раздражающие недостатки Котлина.
Кажется, что единственное разумное решение, полностью поддерживающее обратную совместимость класса, состоит в том, чтобы преобразовать его в обычный класс (а не в класс данных) и реализовать вручную (с помощью IDE) методы: hashCode(), equals(), toString(), copy() и componentN()
class Data3(i: Int)
{
var i: Int = i
override fun equals(other: Any?): Boolean
{
if (this === other) return true
if (other?.javaClass != javaClass) return false
other as Data3
if (i != other.i) return false
return true
}
override fun hashCode(): Int
{
return i
}
override fun toString(): String
{
return "Data3(i=$i)"
}
fun component1():Int = i
fun copy(i: Int = this.i): Data3
{
return Data3(i)
}
}
Я знаю, что это старый вопрос, но, похоже, никто не упоминал о возможности сделать ценность частной и писать пользовательский геттер следующим образом:
data class Test(private val value: Int) {
fun getValue(): Int = if (value < 0) 0 else value
}
Это должно быть совершенно корректным, так как Kotlin не будет генерировать по умолчанию getter для частного поля.
Но в противном случае я определенно согласен с spierce7 в том, что классы данных предназначены для хранения данных, и вам следует избегать жесткой кодировки "бизнес-логики".