【问题标题】:Kotlin Inheritance / SubclassesKotlin 继承/子类
【发布时间】:2021-02-25 14:59:44
【问题描述】:

我是 Kotlin 的新手,来自 JS(但无论如何我是一个非常业余、自学成才的程序员)。过去,我的方法是完成它,但我一直喜欢 Kotlin 的简洁性,并希望避免在我的程序中对类似元素进行不必要的复制。我想知道我是否能够获得有关父类的子类的一些指导。目前,我正在使用两种类型:

class Ratio(var num: Int, var den: Int) {
    var monzo: MutableList<Int> = calculateMonzo(num, den)

    var sizeInCents: Double = calculateCents(num, den)
    var centDeviation: Pair<Double, String> = calculateCentDeviation(num, den, monzo)
    var notation: Triple<String, String, String> = calculateNotation(monzo)
    var frequency: Double = calculateFrequency(num, den)
}

class Monzo(var monzo: List<Int>) {
    var num: Int = calculateRatio(monzo).first
    var den: Int = calculateRatio(monzo).second

    var sizeInCents: Double = calculateCents(num, den)
    var centDeviation: Pair<Double, String> = calculateCentDeviation(num, den, monzo)
    var hejiString: Triple<String, String, String> = calculateNotation(monzo)
    var frequency: Double = calculateFrequency(num, den)
}

基本上,Ratio()Monzo() 都表示相同的“事物”(分数或分数的质因数分解),并且仅取决于用户的首选输入。最后,我计算出“缺失”的信息,然后从那一点开始,属性和计算是相同的。这些将在父类中是有道理的,例如Input(),但我不确定如何最好地设置父类,然后在调用其中一个子类的实例时访问这些父属性。

【问题讨论】:

  • 你不能只有一个类和两个构造函数吗?如果您需要知道它是 Ratio 还是 Monzo,最终添加一个枚举
  • 这真的需要同时存储、返回和操作两种形式的数据(num/den 和int 列表)吗?对我来说,简洁的解决方案是标准化一种形式,只有一个类只存储该形式,然后提供一种从另一种形式创建它的方法(通过辅助构造函数或伴随对象中的工厂方法) 并从中创建其他表单(通过方法或具有自定义 getter 的属性)。
  • @Pawel 和 gidds 我当然可以想象一个单一的标准化。例如,素数分解“monzo”(List)是更有用的形式,任何比率(num/den)都可以通过简单的函数轻松转换为 monzo。有什么方法可以给出一个使用辅助构造函数的非常基本的示例?我对细节感到不安......

标签: class kotlin inheritance


【解决方案1】:

具有两个构造函数的单个类的示例:

class Ratio private constructor(
    val num: Int,
    val den: Int,
    val monzo: List<Int>
) {
    constructor(monzo: List<Int>): this(calculateRatio(monzo).first, calculateRatio(monzo).second, monzo)

    constructor(num: Int, den: Int): this(num, den, calculateMonzo(num, den))

    val sizeInCents: Double = calculateCents(num, den)
    val centDeviation: Pair<Double, String> = calculateCentDeviation(num, den, monzo)
    val notation: Triple<String, String, String> = calculateNotation(monzo)
    val frequency: Double = calculateFrequency(num, den)
}

由于最后四个属性依赖于前三个,它们应该由主构造函数初始化。但是在公共构造函数中拥有所有三个属性将是荒谬的,因为它们相互依赖于彼此的值。因此,将主构造函数设为私有,并允许两个辅助构造函数传递适当的值。

我想不出一个干净的方法来避免重复调用calculateRatio()。我想你可以用这样的伴随对象中的invoke 函数替换该构造函数:

companion object {
    operator fun invoke(monzo: List<Int>) = with(calculateRatio(monzo)) { Ratio(first, second, monzo) }
}

此外,您的所有属性都应为val 而不是var,因为它们取决于比率的初始值。如果您希望numdenmonzo 是可变的,您应该为它们编写自定义设置器来重新计算所有内容。在这种情况下,依赖于比率的属性可以是 var,但使用私有设置器。

【讨论】:

  • 感谢 --> 所以根据使用 Ratio() 时的数据类型,是使用 monzo 构造函数还是使用 num/den 构造函数?我收到必须初始化 num、den 和 monzo 的错误(在构造函数之后的代码中)。我需要某种初始化块或默认值吗?构造函数内部的代码是否在外部剩余代码之前执行?
  • 我认为将 val numval denval monzo 移到构造函数之前,然后将 this. 假装到每个构造函数中的每个引用可以解决问题,但它没有似乎...
  • 我试过玩,但保持相同的语法。当我复制您的示例时,这在逻辑上应该可以满足我的需要,我在构造函数和val 定义本身val num: Intval den: Intval monzo: List&lt;Int&gt; 中都没有错误,但每次都在其余函数中调用numdenmonzo 我收到必须初始化变量的错误。我想我不需要参考主构造函数,因为没有,所以有点茫然......
  • 对不起,我这里漏掉的是执行顺序。属性初始化发生在构造函数块之前,因此当任一构造函数尚未为前三个属性分配任何内容时,它无法计算所有这些依赖属性。所以你毕竟需要一个主构造函数。查看更新后的答案。
  • 太好了,完美运行,感谢您的帮助。我需要阅读更多关于valvar 的信息——这种差异似乎比JS 中constvar 之间的差异更微妙
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多