我们通过一个例子来体会scala对象的构造过程
首先创建一个车站类:
class Station { val size = 100 val spots = new Array[String](size) }
通过size来模拟停车位数,通过字符串数组模拟具体的停车位。
在创建一个停车场类,它是车站的子类
class Park extends Station { override val size = 20 }
停车场没有车站那么多车位,只拥有20个,因此在Park中重写了Station的size成员变量。在Park中也自然继承了station中的spots成员
现在我们执行以下代码:
object Client extends App{ val park = new Park println(park.spots.size) }
输出结果为0
这是为什么呢?
这要从构建对象的过程说起。当创建一个Park对象的时候,会首先创建Park的父类Station对象,在Station对象初始化的时候调用了子类的getSize方法,而子类此时还没有初始化,于是得到了0.
具体步骤如下:
1、调用子类Park构造方法。在调用子类构造方法时首先调用了父类Station的构造方法。
2、Station的构造方法中初始化了size = 100。
3、Station构造方法初始化spots并调用getSize方法获取size。
4、getSize方法被子类Park重写,因此调用Park的getSize方法。
5、Park的getSize方法获取子类size。
6、此时Park对象还没有初始化,因此size = 0。
7、spots被设置为长度为0的数组。
上述两个类用java等效编写的话如下:
public class Station { int size; String[] spots; public Station(){ size = 100; spots = new String[getSize()]; } int getSize(){ return size; } } public class Park extends Station { int size; public Park(){ size = 20; } @Override int getSize() { return size; } }
在scala中想要解决上述问题的方法有以下两种:
1、在父类的成员变量前加上lazy修饰符。这样的话这些变量就会等子类初始化之后再初始化。代码如下:
class Station { val size = 100 lazy val spots = new Array[String](size) }
2、在子类的构造方法中加入参数size并设置默认值,这样在构造父类的时候size的值就已经初始化了。代码如下:
class Park(override val size:Int = 20) extends Station { }
3、在子类中使用提前初始化预发,代码如下:
class Park extends {override val size = 20} with Station { }
上述方式虽然能够解决问题,但是代码并不是非常美观,因此在实际编码时还是要尽量注意在构造方法中不要依赖val成员变量的值。
时间: 2025-01-12 01:19:10