次のKotlinクラスがあるとします。
data class Test(val value: Int)
Int
値が負の場合、0を返すようにゲッターをどのようにオーバーライドしますか?
これが不可能な場合、適切な結果を得るにはどのようなテクニックがありますか?
次のKotlinクラスがあるとします。
data class Test(val value: Int)
Int
値が負の場合、0を返すようにゲッターをどのようにオーバーライドしますか?
これが不可能な場合、適切な結果を得るにはどのようなテクニックがありますか?
回答:
Kotlinを毎日ほぼ1年間執筆した後、このようなデータクラスをオーバーライドしようとすることは悪い習慣であることがわかりました。これには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
}
このアプローチの問題は、データクラスがこのようにデータを変更するためのものではないことです。それらは本当にデータを保持するためだけのものです。このようなデータクラスのgetterをオーバーライドすることを意味するだろうTest(0)
とTest(-1)
考えていないequal
互いに異なるだろうhashCode
Sを、しかし、あなたが呼ばれたとき.value
、彼らは同じ結果を持っているでしょう。これは一貫性がなく、あなたにとってはうまくいくかもしれませんが、これがデータクラスであると見ているチームの他の人々は、それを変更した方法を理解せずに誤って誤用したり、期待どおりに機能しなかったりする可能性があります(つまり、このアプローチではうまくいきません) t Map
またはaで正しく動作しSet
ます)。
data class class(@JsonProperty("iss_position") private val position: Map<String, Double>) { val latitude = position["latitude"]; val longitude = position["longitude"] }
で、私の場合には非常に良いと思います、tbh。これについてどう思う?(他にも多くのフィールドがあったため、コードにネストされたjson構造を再作成することは私には意味がなかったと思います)
parsing a string into an int
、数値クラス以外の文字
List
し、MutableList
理由もなく。
あなたはこのようなことを試すことができます:
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
to の値を割り当ててvalue
います。
説明したロジックを使用して、プロパティのカスタムアクセサーを定義しました。
答えは、実際に使用する機能によって異なりdata
ます。@EPadronは気の利いたトリック(改良版)について言及しました:
data class Test(private val _value: Int) {
val value: Int
get() = if (_value < 0) 0 else _value
}
その意志が期待通りEIことがあり、作品1つの右、フィールド、1つのゲッターを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 value: Int) {
fun getValue(): Int = if (value < 0) 0 else value
}
Kotlinはプライベートフィールドのデフォルトのゲッターを生成しないため、これは完全に有効です。
しかし、それ以外の点では、spierce7に間違いなく同意します。データクラスはデータを保持するためのものであり、そこで「ビジネス」ロジックをハードコーディングすることは避けてください。
val value = test.getValue()
他のゲッターのようにではなく、 このように呼び出す必要があります val value = test.value
.getValue()
私はあなたの答えを見ました。データクラスはデータを保持するためだけのものであることには同意しますが、それらから何かを作成する必要がある場合もあります。
これが私のデータクラスで行っていることです。いくつかのプロパティをvalからvarに変更し、コンストラクターでそれらを上書きしました。
そのようです:
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 { ... }
)を作成することをお勧めします。また、非データクラスを使用すると、プロパティをコンストラクターのパラメーターから分離できるため、データクラスが適切でない場合もあります。クラス定義では、可変性の意図を明示することをお勧めします。とにかくこれらのフィールドも変更可能である場合、データクラスは問題ありませんが、ほとんどすべてのデータクラスは不変です。
これは、(とりわけ)Kotlinの厄介な欠点のようです。
クラスの後方互換性を完全に維持する唯一の合理的な解決策は、それを通常のクラス(「データ」クラスではなく)に変換し、手動で(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)
}
}
私は、次の壊すことなく、あなたが必要なものを達成するための最良の方法であることが判明equals
して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())
しかしながら、
まず、_value
はvar
、ではなくval
、ですが、プライベートクラスであり、データクラスは継承できないため、クラス内で変更されていないことを確認するのは非常に簡単です。
2つ目は、という名前のtoString()
場合と_value
は少し異なる結果を生成しますが、とはvalue
一貫していTestData(0).toString() == TestData(-1).toString()
ます。
_value
initブロックで変更されておりequals
、hashCode
壊れていません。