当子类重写了父类的方法或者字段后,父类又依赖这些字段或者方法初始化,这个时候就会出现问题,比如
class Creature {
val range: Int = 10
val env: Array[Int] = new Array[Int](range)
//lazy val env: Array[Int] = new Array[Int](range)
}
class Ant extends Creature {
//override val range = 2
override val range = 2
}
-------------------------------------------------------------
val ant = new Ant
println(ant.range)
println(ant.env.length)
此时的构造顺序为
(1)Ant的构造器在调用它自己的构造之前,调用Creature的构造器
(2)Creature的构造器将它的range字段设为10
(3)Creature的构造器为了初始化env数组,调用range()取值器
(4)range该方法被重写以输出(还未初始化的)Ant类的range字段值
(5)range方法返回0,(这是对象被分配空间时所有整形字段的初始值)
(6)env被设为长度为0的数组。
(7)Ant构造器继续执行,将其range字段设为2.
那么env的大小是多少?是0,惊不惊喜,意不意外?
问题解决,4种方案
(1)子类不重写父类
(2)可以将val声明为final,这样子类不可改写。
(3)可以将超类中将val声明为lazy,这样安全但并不高效。
(4)还可以使用提前定义语法,可以在超类的构造器执行之前初始化子类的val字段
提前定义,颠覆三观的方法来了,哈哈 ~ ~
看着很像多继承,但是它不是多继承
class Ant2 extends {
override val range = 3
} with Creature