提问人:pbuchheit 提问时间:11/17/2023 更新时间:11/17/2023 访问量:38
初始化 Kotlin 数据类的主构造函数中的属性
Initializing properties in the primary constructor of a Kotlin data class
问:
我正在做一个需要处理“有理”数字的 Kotlin 项目。为此,我有一个类,它将有理数存储为两个整数(分子和分母),并使用这些值执行一些算术运算。我遇到的问题是,其中一个要求是将值规范化。传入值 2/4 应存储为 1/2,6/8 应存储为 1/4,依此类推。我还想把它做成一个“数据类”,这样我就可以利用内置的 equals 和 hashcode 函数。
我的类如下所示:
data class Rational(val numerator: BigInteger, val denominator: BigInteger): Comparable<Rational>
{
init {
if(denominator.equals(0))
{
throw IllegalArgumentException("0 is not a valid denominator")
}
//normalize the value by reducing to the least common denominator
val gcd = numerator.gcd(denominator)
this.numerator = numerator.div(gcd)
this.denominator = denominator.div(gcd)
}
}
infix fun BigInteger.divBy(d: BigInteger): Rational
{
return Rational(this, d)
}
fun main() {
val half = 2 divBy 4
println(half) // expected: Rational(numerator=1, denominator=2)
}
由于参数为“val”,因此无法编译。我不想使属性可变,但我不确定在设置值之前如何进行计算。我无法删除修饰符,因为它们是数据类所必需的。
初始化在设置值之前需要处理的属性的过程是什么?到目前为止,我能找到的唯一答案是:如何在初始化 Kotlin 对象时存储临时变量? 这似乎是针对 Kotlin 的旧版本(1.0 之前的版本)。
答:
我相信这不可能直接实现。根据设计,数据类是一个简单的数据容器。如果我们向它传递一个值,我们希望它恰好持有这个值。动态转换值可能被视为意外行为。也许提供成员函数或工厂函数会更好?normalized()
话虽如此,我们可以通过隐藏主构造函数并为伴随对象提供运算符来欺骗它:invoke()
fun main() {
println(Rational(4.toBigInteger(), 8.toBigInteger())) // Rational(numerator=1, denominator=2)
}
data class Rational private constructor(val numerator: BigInteger, val denominator: BigInteger) {
companion object {
operator fun invoke(numerator: BigInteger, denominator: BigInteger): Rational {
if(denominator.equals(0))
{
throw IllegalArgumentException("0 is not a valid denominator")
}
//normalize the value by reducing to the least common denominator
val gcd = numerator.gcd(denominator)
return Rational(numerator.div(gcd), denominator.div(gcd))
}
}
}
当然,这并不完全是你所要求的。我们在这里不使用构造函数,而使用看起来像构造函数的函数。另外,我们需要记住不进行规范化的函数,我相信我们不能覆盖它。copy()
如果以后有人遇到这种情况,我最终更改为非数据类并自己实现 equals 和 hashmap 函数。最终结果如下所示:
class Rational ( numerator: BigInteger, denominator: BigInteger): Comparable<Rational>
{
private val numerator: BigInteger
private val denominator: BigInteger
init {
require(denominator!=BigInteger.ZERO)
{
throw IllegalArgumentException("0 is not a valid denominator")
}
//normalize the value by reducing to the least common denominator and simplifying negation
val sign: BigInteger = denominator.signum().toBigInteger()
val gcd = numerator.gcd(denominator)
this.numerator = sign * numerator.div(gcd)
this.denominator = sign * denominator.div(gcd)
}
override fun toString(): String {
if(denominator == BigInteger.valueOf(1))
{
return numerator.toString()
}
return "$numerator/$denominator"
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Rational
if (numerator != other.numerator) return false
if (denominator != other.denominator) return false
return true
}
override fun hashCode(): Int {
var result = numerator.hashCode()
result = 31 * result + denominator.hashCode()
return result
}
}
上一个:通过多重继承继承特定属性
下一个:创建简单的 c 类 [已关闭]
评论
copy
data
equals()
hashCode()
toString()
component0()
component1()
copy()
Comparable
Number