在Kotlin数据类的主构造函数中初始化属性

ifsvaxew  于 12个月前  发布在  Kotlin
关注(0)|答案(2)|浏览(130)

我正在做一个需要处理“有理数”的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',所以无法编译。我不想让属性可变,但我不确定在设置值之前如何进行计算。我不能删除修饰符,因为它们是数据类所必需的。
在设置值之前需要处理的属性的初始化过程是什么?到目前为止我能找到的唯一答案是:How to store temporary variable while initializing a Kotlin object?,它似乎是用于旧版本(1.0之前)的Kotlin。

izkcnapc

izkcnapc1#

我相信这是不可能的。根据设计,数据类是一个简单的数据保持器。如果我们向它传递一个值,我们希望它正好保存这个值。动态转换值可能被认为是一个意外的行为。也许提供一个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()函数,它没有进行规范化,我相信我们不能覆盖它。

esbemjvw

esbemjvw2#

为了防止以后有人遇到这种情况,我最终改为非数据类,并自己实现了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
    }
}

字符串

相关问题