Angesichts der folgenden Kotlin-Klasse:
data class Test(val value: Int)
Wie würde ich den Int
Getter überschreiben, damit er 0 zurückgibt, wenn der Wert negativ ist?
Wenn dies nicht möglich ist, mit welchen Techniken kann ein geeignetes Ergebnis erzielt werden?
Antworten:
Nachdem ich fast ein ganzes Jahr lang täglich Kotlin geschrieben habe, habe ich festgestellt, dass der Versuch, Datenklassen wie diese zu überschreiben, eine schlechte Praxis ist. Hierfür gibt es drei gültige Ansätze. Nachdem ich sie vorgestellt habe, erkläre ich, warum der Ansatz, den andere Antworten vorgeschlagen haben, schlecht ist.
Lassen Sie Ihre Geschäftslogik, die
data class
den Wert erstellt , den Wert auf 0 oder höher ändern, bevor Sie den Konstruktor mit dem falschen Wert aufrufen. Dies ist wahrscheinlich der beste Ansatz für die meisten Fälle.Verwenden Sie keine
data class
. Verwenden Sie eine reguläre Methode und lassen Sieclass
Ihre IDE die Methodenequals
undhashCode
für Sie generieren (oder nicht, wenn Sie sie nicht benötigen). Ja, Sie müssen es neu generieren, wenn eine der Eigenschaften des Objekts geändert wird, aber Sie haben die vollständige Kontrolle über das Objekt.Erstellen Sie eine zusätzliche sichere Eigenschaft für das Objekt, die das tut, was Sie möchten, anstatt einen privaten Wert zu haben, der effektiv überschrieben wird.
Ein schlechter Ansatz, den andere Antworten vorschlagen:
Das Problem bei diesem Ansatz ist, dass Datenklassen nicht wirklich dazu gedacht sind, solche Daten zu ändern. Sie dienen wirklich nur zum Speichern von Daten. Das Überschreiben des Getters für eine Datenklasse wie diese würde dies bedeuten
Test(0)
undTest(-1)
würde sich nichtequal
gegenseitig beeinflussen und unterschiedlichehashCode
s haben, aber wenn Sie anrufen.value
, würden sie das gleiche Ergebnis haben. Dies ist inkonsistent, und obwohl es für Sie möglicherweise funktioniert, können andere Personen in Ihrem Team, die dies als Datenklasse ansehen, es versehentlich missbrauchen, ohne zu bemerken, wie Sie es geändert haben / es nicht wie erwartet funktionieren lassen (dh dieser Ansatz würde nicht funktionieren). in aMap
oder aSet
) nicht richtig funktionieren .quelle
data class class(@JsonProperty("iss_position") private val position: Map<String, Double>) { val latitude = position["latitude"]; val longitude = position["longitude"] }
, und ich halte es für ziemlich gut für meinen Fall, tbh. Was denkst du darüber? (Es gab viele andere Felder und daher glaube ich, dass es für mich keinen Sinn machte, diese verschachtelte JSON-Struktur in meinem Code neu zu erstellen.)parsing a string into an int
erlauben Sie eindeutig die Geschäftslogik des Parsens und der Fehlerbehandlung nicht numerischer Zeichenfolgen in Ihrer Modellklasse ...List
undMutableList
ohne Grund.Sie könnten so etwas versuchen:
In einer Datenklasse müssen Sie die Parameter des primären Konstruktors mit entweder
val
oder markierenvar
.Ich weise den Wert von
_value
zuvalue
zu, um den gewünschten Namen für die Eigenschaft zu verwenden.Ich habe einen benutzerdefinierten Accessor für die Eigenschaft mit der von Ihnen beschriebenen Logik definiert.
quelle
Die Antwort hängt davon ab, welche Funktionen Sie tatsächlich verwenden
data
. @ EPadron erwähnte einen raffinierten Trick (verbesserte Version):Das wird wie erwartet funktionieren, ei es hat ein Feld, einen Getter, richtig
equals
,hashcode
undcomponent1
. Der Haken ist dastoString
undcopy
sind komisch:Um das Problem zu beheben
toString
, können Sie es von Hand neu definieren. Ich kenne keine Möglichkeit, die Parameternamen zu korrigieren, aber überhaupt nicht zu verwendendata
.quelle
Ich weiß, dass dies eine alte Frage ist, aber es scheint, dass niemand die Möglichkeit erwähnt hat, Wert privat zu machen und einen benutzerdefinierten Getter wie diesen zu schreiben:
Dies sollte vollkommen gültig sein, da Kotlin keinen Standard-Getter für private Felder generiert.
Ansonsten stimme ich spierce7 definitiv zu, dass Datenklassen zum Speichern von Daten dienen und Sie es vermeiden sollten, die "Geschäfts" -Logik dort fest zu codieren.
quelle
val value = test.getValue()
und nicht wie andere Getterval value = test.value
.getValue()
Ich habe Ihre Antwort gesehen, ich stimme zu, dass Datenklassen nur zum Speichern von Daten gedacht sind, aber manchmal müssen wir etwas daraus machen.
Folgendes mache ich mit meiner Datenklasse: Ich habe einige Eigenschaften von val in var geändert und sie im Konstruktor überschrieben.
wie so:
quelle
fun Recording(...): Recording { ... }
). Vielleicht ist eine Datenklasse auch nicht das, was Sie wollen, da Sie mit Nicht-Datenklassen Ihre Eigenschaften von Ihren Konstruktorparametern trennen können. Es ist besser, Ihre Veränderungsabsichten in Ihrer Klassendefinition explizit anzugeben. Wenn diese Felder sowieso auch veränderbar sind, ist eine Datenklasse in Ordnung, aber fast alle meine Datenklassen sind unveränderlich.Dies scheint (unter anderem) ein ärgerlicher Nachteil von Kotlin zu sein.
Es scheint, dass die einzig vernünftige Lösung, die die Abwärtskompatibilität der Klasse vollständig gewährleistet, darin besteht, sie in eine reguläre Klasse (keine "Daten" -Klasse) zu konvertieren und die folgenden Methoden (mit Hilfe der IDE) manuell zu implementieren: hashCode ( ), equals (), toString (), copy () und componentN ()
quelle
Ich fand, dass der folgende Ansatz der beste ist, um das zu erreichen, was Sie brauchen, ohne zu brechen
equals
undhashCode
:Jedoch,
Zuerst beachten Sie, dass
_value
istvar
, nichtval
, aber auf der anderen Seite, da es nicht privat und Datenklassen ist vererbt werden aus, es ziemlich einfach ist , sicher zu stellen , dass es nicht innerhalb der Klasse geändert wird.Zweitens
toString()
ergibt sich ein etwas anderes Ergebnis als bei einem_value
Namenvalue
, aber es ist konsistent undTestData(0).toString() == TestData(-1).toString()
.quelle
_value
wird im Init-Block geändertequals
undhashCode
ist nicht defekt.