子类var初始化步骤和OOP结构设计

时间:2018-08-20 18:27:38

标签: oop inheritance methods kotlin override

抽象超类Parent具有抽象方法extract(Int),它从主构造函数中获取值a,并将其提交给该方法extract(a)

abstract class Parent ( val a : Int ) {

    abstract fun extract ( i : Int )

    init {
        // call overriden extract(Int)
        extract ( a )
    }
}

子类Child定义方法extract(Int),将值b发送到Parent的构造函数,然后父类Parent调用{ {1}}将该值设置为extract(Int)的变量Child

x

测试:

class Child ( b : Int ) : Parent ( b ) {

    // initialize x variable to -1
    var x : Int = -1

    override fun extract ( i : Int ) {
        // set x to given int
        x = i
    }
}

得到了:

println ( Child ( 10 ).x )

现在,尝试添加-1 子类中的init{}

Child

再次测试:

    init {
        x = -2
    }

得到了:

println ( Child ( 10 ).x )

表面上,子类-2 的{​​{1}}是在超级类init{}的构造之后完成的。

应该{​​{1}}被所有子类而不是超类所覆盖吗?

2 个答案:

答案 0 :(得分:0)

我不确定“超级类Child”的含义,但似乎您可能对派生类的顺序感到困惑。这是一个非常好的example that shows the order。它具有您可以在网站上运行的代码段。

答案 1 :(得分:0)

通常来讲,这种初始化程序交互是令人讨厌的,因为尽管定义明确,但操作的确切顺序通常是违反直觉的。

具体来说,我相信在这里发生的事情,使用kotlin中更详细的显式构造函数语法可能会更清楚。您的代码是此代码的缩写版本(请注意,这是合法的Kotlin):

abstract class Parent {

    val a: Int

    constructor(a: Int){
        this.a = a
        extract(a)
    }

    abstract fun extract ( i : Int )
}

class Child: Parent {

    var x : Int

    constructor(b: Int): Parent(b) {
        //super.constructor(b) fires here

        // initialize x variable to -1 
        x = -1
    }

    override fun extract (i : Int) {
        // set x to given int
        x = i
    }
}

因此,我希望现在更加清楚了,伪呼叫跟踪如下:

  
      
  1. 输入Child.constructor(10)
  2.   
  3. 输入Parent.constructor(10)
  4.   
  5. 将10分配给this@Parent.a
  6.   
  7. 输入this.extract(10) => V表解析Child.extract()
  8.   
  9. 将10分配给this@Child.x
  10.   
  11. extract返回
  12.   
  13. Parent.constructor返回
  14.   
  15. 将-1分配给this@Child.x 这可能是您的困惑点
  16.   
  17. Child.constructor返回
  18.   

你能做什么

通常来说,当您遇到混乱的初始化流程时,JVM的答案是使用另一种方法而不是更多的initconstructors形式化复杂的初始化

在kotlin中,最简单的方法(以及许多图书馆使用的策略)是创建静态工厂方法。

abstract class Parent(val a: Int) {
  //...
}

class Child private constructor(var x: Int): Parent(x) {

    companion object {

        fun makeChild(unextracted: Int) {
            val extracted = extract(unextracted)
            return Child(extracted)
        }

        // a first good step is to make `extract` referentially transparent 
        // aka pure
        // IE all of its function is provided in its return type
        fun extract (i : Int): Int {
            return i //do appropriate transforms.
            //note, if you need multiple return values here, consider Pairs or Triples. 
        }
    }
}
  

请注意,kotlin为此提供了一些语法糖,您可以覆盖invoke操作符:

     

class Child { companion object { operator fun invoke(x: Int) { ... } } }

     

,这意味着要使用〜constructor语法(makeChild)来代替Child.makeChild(10)(例如Child(10))来调用工厂函数。

更一般而言,如果您发现由于某种原因必须使用初始化程序流而在一个IOC容器下遇到此问题,我建议您重构IOC使用者以使用旧的-老式的Java工厂。在这里,我需要更多详细信息。