Aşağıdaki Kotlin sınıfı verildiğinde:
data class Test(val value: Int)
Int
Değer negatifse 0 döndürmesi için alıcıyı nasıl geçersiz kılabilirim ?
Bu mümkün değilse, uygun bir sonuç elde etmek için bazı teknikler nelerdir?
Aşağıdaki Kotlin sınıfı verildiğinde:
data class Test(val value: Int)
Int
Değer negatifse 0 döndürmesi için alıcıyı nasıl geçersiz kılabilirim ?
Bu mümkün değilse, uygun bir sonuç elde etmek için bazı teknikler nelerdir?
Yanıtlar:
Neredeyse bir yıl boyunca Kotlin'i günlük yazmakla geçirdikten sonra, bunun gibi veri sınıflarını geçersiz kılmaya çalışmanın kötü bir uygulama olduğunu gördüm. Bunun için 3 geçerli yaklaşım var ve bunları sunduktan sonra, diğer yanıtların önerdiği yaklaşımın neden kötü olduğunu açıklayacağım.
Yapıcıyı data class
kötü değerle aramadan önce alter değerini 0 veya daha büyük olacak şekilde yaratan iş mantığınız olsun . Bu, çoğu durumda muhtemelen en iyi yaklaşımdır.
Bir data class
. Bir normal kullanın class
ve IDE'nizin sizin için equals
ve hashCode
yöntemlerini oluşturmasını sağlayın (veya ihtiyacınız yoksa yapmayın). Evet, nesnedeki özelliklerden herhangi biri değiştirilirse, ancak nesnenin tam kontrolü size bırakılırsa onu yeniden oluşturmanız gerekir.
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()
}
}
Etkin bir şekilde geçersiz kılınan özel bir değere sahip olmak yerine, istediğiniz şeyi yapan nesne üzerinde ek bir güvenli özellik oluşturun.
data class Test(val value: Int) {
val safeValue: Int
get() = if (value < 0) 0 else value
}
Diğer yanıtların önerdiği kötü bir yaklaşım:
data class Test(private val _value: Int) {
val value: Int
get() = if (_value < 0) 0 else _value
}
Bu yaklaşımla ilgili sorun, veri sınıflarının gerçekten bu gibi verileri değiştirmeye yönelik olmamasıdır. Gerçekten sadece veri tutmak içindir. Böyle bir veri sınıfı için toplama maddesinin geçersiz kılma anlamına gelir Test(0)
ve Test(-1)
olmaz equal
birbirlerini ve farklı olurdu hashCode
s, ama beni aradığında .value
, onlar da aynı sonuca ulaşılır. Bu tutarsızdır ve sizin için işe yarayabilirken, ekibinizdeki bunun bir veri sınıfı olduğunu gören diğer kişiler, onu nasıl değiştirdiğinizi / beklendiği gibi çalışmadığını fark etmeden yanlışlıkla onu kötüye kullanabilir (yani bu yaklaşım olmaz '' t a Map
veya a Set
) ' da doğru çalışır .
data class class(@JsonProperty("iss_position") private val position: Map<String, Double>) { val latitude = position["latitude"]; val longitude = position["longitude"] }
, az önce yazdım ve davam için oldukça iyi olduğunu düşünüyorum, tbh. Bunun hakkında ne düşünüyorsun? (orada diğer alanlarda ofc vardı ve dolayısıyla bu benim kodunda bu iç içe json yapısı oluşturulana kadar bana hiç mantıklı inanmak)
parsing a string into an int
, açıkça ayrıştırma ve hata modeli sınıfa sayısal olmayan dizeler taşıma iş mantığı izin veriyorsunuz ...
List
ve MutableList
sebepsiz yere.
Bunun gibi bir şey deneyebilirsin:
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
Bir veri sınıfında, birincil kurucunun parametrelerini val
veya ile işaretlemeniz gerekir var
.
Ben değerini tahsis edeceğim _value
için value
özellik için istediğiniz adı kullanmak için.
Tanımladığınız mantıkla özellik için özel bir erişimci tanımladım.
Cevap, gerçekte hangi yetenekleri kullandığınıza bağlıdır data
. @EPadron şık bir numaradan bahsetti (geliştirilmiş versiyon):
data class Test(private val _value: Int) {
val value: Int
get() = if (_value < 0) 0 else _value
}
O irade beklendiği gibi ei sahip olduğu, çalışır bir sağ, alan, tek getter equals
, hashcode
ve component1
. Yakalama şu toString
ve copy
tuhaf:
println(Test(1)) // prints: Test(_value=1)
Test(1).copy(_value = 5) // <- weird naming
toString
Sizinle sorunu çözmek için elle yeniden tanımlayabilirsiniz. Parametre isimlendirmesini düzeltmenin bir yolunu bilmiyorum ama kullanmamam data
.
Bunun eski bir soru olduğunu biliyorum ama görünen o ki kimse değeri özel yapma ve böyle özel alıcı yazma olasılığından bahsetmedi:
data class Test(private val value: Int) {
fun getValue(): Int = if (value < 0) 0 else value
}
Kotlin özel alan için varsayılan alıcı oluşturmayacağından bu tamamen geçerli olmalıdır.
Ama aksi takdirde, veri sınıflarının veri tutmak için olduğu ve orada "iş" mantığını kodlamaktan kaçınmanız gerektiği konusunda spierce7'ye kesinlikle katılıyorum.
val value = test.getValue()
ve diğer alıcılar gibi değil val value = test.value
.getValue()
Cevabınızı gördüm, veri sınıflarının yalnızca veri tutmak için olduğunu kabul ediyorum, ancak bazen bunlardan bir şeyler yapmamız gerekiyor.
İşte veri sınıfımla yaptığım şey, bazı özellikleri val'den var'a değiştirdim ve yapıcıda bunları geçersiz kıldım.
bunun gibi:
data class Recording(
val id: Int = 0,
val createdAt: Date = Date(),
val path: String,
val deleted: Boolean = false,
var fileName: String = "",
val duration: Int = 0,
var format: String = " "
) {
init {
if (fileName.isEmpty())
fileName = path.substring(path.lastIndexOf('\\'))
if (format.isEmpty())
format = path.substring(path.lastIndexOf('.'))
}
fun asEntity(): rc {
return rc(id, createdAt, path, deleted, fileName, duration, format)
}
}
fun Recording(...): Recording { ... }
) olarak işlev gören bir işlev oluşturmak daha iyi olur . Ayrıca veri sınıfı olmayan sınıflarla özelliklerinizi yapıcı parametrelerinizden ayırabileceğiniz için bir veri sınıfı da istediğiniz şey olmayabilir. Sınıf tanımınızda değişkenlik niyetleriniz konusunda açık olmak daha iyidir. Bu alanlar yine de değiştirilebilirse, o zaman bir veri sınıfı iyidir, ancak neredeyse tüm veri sınıflarım değişmezdir.
Bu, Kotlin'in (diğerlerinin yanı sıra) can sıkıcı dezavantajlarından biri gibi görünüyor.
Görünüşe göre, sınıfın geriye dönük uyumluluğunu tamamen koruyan tek makul çözüm, onu normal bir sınıfa ("veri" sınıfına değil) dönüştürmek ve elle (IDE'nin yardımıyla) şu yöntemleri uygulamaktır: hashCode ( ), equals (), toString (), copy () ve 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)
}
}
İhtiyaç duyduğunuz şeyi bozmadan elde etmek için en iyi yaklaşımın aşağıdakiler olduğunu buldum equals
ve hashCode
:
data class TestData(private var _value: Int) {
init {
_value = if (_value < 0) 0 else _value
}
val value: Int
get() = _value
}
// Test value
assert(1 == TestData(1).value)
assert(0 == TestData(-1).value)
assert(0 == TestData(0).value)
// Test copy()
assert(0 == TestData(-1).copy().value)
assert(0 == TestData(1).copy(-1).value)
assert(1 == TestData(-1).copy(1).value)
// Test toString()
assert("TestData(_value=1)" == TestData(1).toString())
assert("TestData(_value=0)" == TestData(-1).toString())
assert("TestData(_value=0)" == TestData(0).toString())
assert(TestData(0).toString() == TestData(-1).toString())
// Test equals
assert(TestData(0) == TestData(-1))
assert(TestData(0) == TestData(-1).copy())
assert(TestData(0) == TestData(1).copy(-1))
assert(TestData(1) == TestData(-1).copy(1))
// Test hashCode()
assert(TestData(0).hashCode() == TestData(-1).hashCode())
assert(TestData(1).hashCode() != TestData(-1).hashCode())
Ancak,
İlk olarak, not _value
olduğu var
değil, val
onu özel ve o oldukça kolay emin sınıfın içinde değiştirilmez emin olmak için var, veri sınıfları miras olamaz çünkü, ama diğer taraftan.
İkincisi, adlandırıldığından toString()
biraz farklı bir sonuç üretir , ancak tutarlıdır ve ._value
value
TestData(0).toString() == TestData(-1).toString()
_value
init bloğunda değiştirilmiş ve equals
ve hashCode
kırık değildir.