本文我们聊聊Scala类的单继承/多继承、private/public关键字问题。
1.1 首先,来看下多继承、单继承是什么意思,区别是什么?
多继承:A类、B类是两个没有继承关系的类,C类即继承了A类又继承了B类。
print('---------------------多继承------------------------')
class A:
pass
class B:
pass
class C(A, B):
pass
单继承:C类继承了B类,B类继承了A类,C、B、A是层次继承的关系。
print('---------------------单继承------------------------')
class Animal:
pass
class Animal1(Animal):
pass
1.2 多继承相比于单继承有什么问题?为什么Scala采用单继承,而不是多继承?
举一个多重继承的例子。蝙蝠即有鸟类的一些特征,又有哺乳动物类的一些特征。鸟类和哺乳类都有“繁殖”这个方法。
如果蝙蝠采用的是多重继承的方法,继承了鸟类和哺乳类,那么当我们调用“繁殖”这个方法的时候,冲突就出现了。它不知道该是用鸟类的“繁殖”方法,还是哺乳类的“繁殖”方法。
这就是多重继承的冲突问题。
我们知道Scala基于Java的开发语言,很多设计都来源于JAVA。在JAVA中,为了解决多重继承的冲突问题,给出的解决方法是:一个物体的本质只能有一个。这就是单继承背后的核心思想。
一个子类只能继承一个父类,但可以继承多个接口 或特质。
我们上面蝙蝠的例子就是它只能是鸟类或哺乳类中的一种,但你可以创造一个接口来满足这个父类中没有的方法。或者在蝙蝠这个类中新增父类中没有的方法。当然你也可以对父类中已有的方法进行重写override,比如可飞行但没有羽毛。通过这样的方式来得到即有鸟类,又有哺乳类特征的蝙蝠这个类。
所以,单继承即实现了需要的类,又能避免多重继承的冲突问题,这就是Scala采用单继承而不是多重继承的原因。
1.3 抽象类abstract class 和 特质trait(或接口interface)的区别,及其在类继承中的应用
以下4条基线可以参考:
1.4 特质trait的多继承顺序问题 (注:查看我的博文 10、Scala特质trait)
准则:
2.1 关键字private / public 代表什么意思?不写的时候默认是指哪个?
类中成员默认是公有(public
)的。使用private
修饰则定义的是私有成员(属性、方法),这样它们在类外部就不能被调用,即只能在类内部调用,外部调用会报错。
另外,类的主构造方法中带有val
和var
的参数是公有的。
class Point(val x: Int, val y: Int)
类的主构造方法中不带val
或var
的参数是私有的,仅在类中可见。
class Point(x: Int, y: Int)
val point = new Point(1, 2)
point.x // <-- does not compile
关键字的代码示例如下:
(1)私有属性和方法不能在外部被调用,但可以通过内部的方法调用。
(2)类中公有的、可变的属性可以通过外部调用,可以被修改。
注意: 如果代码里的class名称改成Test,使之变成对象Test的伴生类,那么还是可以调用类的私有变量和私有方法的,因为定义里类和它的伴生对象可以互相访问其私有成员。
一般使用伴生对象来定义那些在伴生类中不依赖于实例化对象而存在的成员变量或者方法。
2.3 什么时候使用关键字?
取决于你是否希望这个属性或方法在类的外部被调用。
如果我们写的类不是提供给其他人用的接口这种用途,是可以不用私有变量的。但如果是作为接口提供或被多处引用,则就要考虑哪些是内部私有属性/方法,只允许内部访问,不允许外部访问;哪些是外部公有的属性/方法,允许外部调用和方法。
同时也要思考,哪些公有属性是可变的,允许外部重新赋值修改,哪些公有属性是不可变的,只允许外部访问,但不能修改。
scala为每个字段生成getter与setter方法。这样属性或方法可以被外部调用、或者被重新赋值/重写等。