第九章 继承

扩展类

scala扩展类的方式和Java一样,使用 extends 关键字:

class Employee extends Person{
    val salary = 0.0
...
}

和Java一样,你可以将类声明为 final ,这样它就不能被扩展。你可以将单个方法或者字段声明为 final , 以确保它们不能够被重写。

重写方法

在scala中重写一个非抽象方法必须使用 override 修饰符。

public class Person{
...
override def toString = getClass.getName + "[name=" + name + "]"
}

override 修饰符可以用在多个情况下给出有用的错误提示。包括:

  • 当你拼错要重写的方法名
  • 当你不小心在新方法中使用错误的参数类型。
  • 当你在超类中引入新的方法,而这个新的方法与子类的方法相抵触。

在scala中调用超类的方法和Java一样,使用 super 关键字。

类型转换和检查

要检查某个对象是否属于某个特定的类,可以用 isInstanceOf 方法。如果测试成功,你就可以使用 asInstanceOf 方法将引用转换为子类的引用。

if (p.isInstanceOf[Employee]){
val s = p.asInstanceOf[Employee]
}

如果 p 指向的是 Employee 类及其子类,则 p.isInstanceOf[Employee] 就会成功;

如果 pnull , 则 p.isInstanceOf[Employee] 将返回 false ,且 p.asInstanceOf[Employee] 将返回 null

如果 p 不是一个 Employee ,则 p.asInstanceOf[Employee] 将抛出异常。

如果你想要测试 p 指向的是一个 Employee 对象但又不是其子类的话,可以使用:

if (p.getClass == classOf[Employee])

不过与类型检查和转化相比,模式匹配通常是更好的选择。

p match{
case s:Employee => ...
case _ =>...
}

受保护字段

如果字段或方法被声明为 protected ,则这样的成员可以被任何子类访问,但不能从其他位置看到。与Java不同的是,protected 的成员对于类所属的包而言,是不可见的。

超类的构造

辅助构造器永远不能够直接调用超类的构造器,子类的辅助构造器最终都会调用主构造器,只有主构造器可以调用超类的构造器。

scala类可以扩展Java类,这种情况下,它的主构造器必须调用Java超类的某一个构造方法。

重写字段

注意以下限制:

  • def 只能重写另一个 def
  • val 只能重写另一个 val 或不带参数的 def
  • var 只能重写另一个抽象的 var

匿名子类

和Java一样,你可以通过包含带有定义或者重写代码块的方式创建一个匿名子类。

val align = new Person("Fred"){
def greeting = "hello"
}

抽象类

和Java一样,可以使用 abstract 关键字定义一个不能被实例化的类。

abstract class Person(val name:String){
def id:Int
}

但是在scala中,不像java,你不需要对抽象方法使用 abstract 关键字,你只是省去方法体。

在子类中重写父类的抽象方法时,你不需要使用 override 关键字。

抽象字段

除了抽象方法之外,类还可以拥有抽象字段,抽象字段就是一个没有初始化值的字段。

具体的子类必须提供具体的字段。和方法一样,在子类中重写超类的抽象字段时,不需要 override 关键字。

scala继承层级

所有其他类都是 AnyRef 的子类, AnyRef 相当于Java中的 Object 类。

AnyValAnyRef 都扩展自 Any 类,而 Any 类是整个继承层级的根节点。

Null 类型的唯一实例就是 null 值,你可以额将 null 值赋值给任何引用,但不能赋值给值类型的应用。

Nothing 类型没有实例,它对于泛型结构时常有用。

对象相等性

在scala中, AnyRefeq 方法检查两个引用是否指向同一个对象。 AnyRefequals 方法调用 eq