抽象超类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}}被所有子类而不是超类所覆盖吗?
答案 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
}
}
因此,我希望现在更加清楚了,伪呼叫跟踪如下:
- 输入
Child.constructor(10)
- 输入
Parent.constructor(10)
- 将10分配给
this@Parent.a
- 输入
this.extract(10)
=> V表解析Child.extract()
- 将10分配给
this@Child.x
extract
返回Parent.constructor
返回- 将-1分配给
this@Child.x
这可能是您的困惑点Child.constructor
返回
通常来说,当您遇到混乱的初始化流程时,JVM的答案是使用另一种方法而不是更多的init
或constructors
形式化复杂的初始化
在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工厂。在这里,我需要更多详细信息。