我正在开发一个需要处理“有理”数的 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 对象时存储临时变量? 这似乎适用于旧版(1.0 之前)的 kotlin。
我相信这不是直接可能的。按照设计,数据类是一个简单的数据持有者。如果我们向它传递一个值,我们期望它准确地保存这个值。即时转换值可能被视为意外行为。也许提供一个
normalize()
成员函数或 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()
函数不进行标准化,我相信我们不能覆盖它。