继承
知识点
继承类 extends
重写方法时必须用override
只有主构造器可以调用超类的主构造器
重写字段
抽象类、字段
扩展类
使用extends关键字
class Employee extends Person {...}
在子类中定义需要的新字段和方法,或者重写超类的方法
可将类声明为final,这样就不有被扩展
可将单个方法或字段声明为final,确保不能被重写
在上一节中,类有一个主构造器和任意多的辅助构造器,而每个辅助构造器都必须先调用先前定义的辅助构造器或主构造器为开始,调用超类构造器的方式也同样与类的定义交织在一起
class Employee(name: String, age: Int, val salary: Double) extends Person(name, age) {
}
说明:Employee类中有三个参数:name、age和salary,其中的name和age会被传递到超类中。相当于下面的Java代码
public class Employee extends Person {
private String name;
private int age;
private double salary;
public Employee(String name, int age, double salary) {
super(name, age);
this.salary = salary;
}
}
重写方法
在Scala中重写一个非抽象方法必须使用override修饰符
class Pseron{
override def toString = getClass.getName
}
在Scala中调用超类的方法用supper关键字
class Employee extends Pseron{
override def toString = super.toString + getClass.getName
}
重写字段
可以用另一个同名的val字段重写一个val或不带参数的def
常见操作是用val重写抽象的def
abstract class Person{
def id: Int
}
class Student(override val id: Int) extends Person
重写时注意如下限制
def只能重写另一个def
val只能重写另一个val或不带参数的def
var只能重写另一个抽象的val
抽象类
用abstract关键字标记的类不能被实例化
抽象类通常包含某个或几个方法没有被完整定义
与Java不同,不需要对抽象方法使用abstract关键字,只是省去了方法体
如果某个类至少存在一个抽象方法,则该类必须声明为abstract
子类中重写超类中抽象方法时,不需要使用override关键字
抽象字段
抽象字段就是一个没有初始值的字段
abstract class Person{
val id: Int
var name: String
}
具体的子类必须提供具体的字段
和方法一样,在子类中重写超类中的抽象字段时,不需要override关键字
特质
知识点
Scala和Java一样不允许类继承多个超类,特质解决这一局限性
类可以实现任意数量的特质
当将多个特质叠加在一起时,顺序很重要,其方法先被执行的特质排在更后面
Scala特质可以提供方法和字段的实现
特质要求实现它们的类具备特定的字段、方法或超类
特质可以同时拥有抽象方法和具体方法,而类可以实现多个特质
当做接口使用的特质
Scala特质完全可以像Java的接口一样,使用关键字 trait
不需要将方法声明为abstract,特质中未被实现的方法默认就是抽象的
在子类中重写特质的抽象方法不需要用 override 关键字
package com.gemantic.base
/**
* @author Yezhiwei
* @date 18/1/6
*/
object TraitLearn {
def main(args: Array[String]): Unit = {
val logger = new ConsoleLogger
logger.log("console log message...")
}
}
trait Logger {
def log(msg: String)
}
class ConsoleLogger extends Logger {
override def log(msg: String): Unit = println(msg)
}
说明:
子类实现特质,用 extends 而不是 implements
不需要写 override
如果需要多个特质,可以用with关键字来添加额外的特质,如下代码
class ConsoleLogger extends Logger with Serializable {
override def log(msg: String): Unit = println(msg)
}
带有具体实现的特质
在Scala的特质中的方法并不需要一定是抽象的
子类从特质得到了一个具体的log方法实现
trait ConsoleLoggerImp {
def log(msg: String) {println(msg)}
}
class AccountAction extends Account with ConsoleLoggerImp {
def withdraw(amount: Double): Unit = {
if (amount > nowBalance) {
log("insufficient funds")
} else {
log("enough funds")
}
}
}
object TraitLearn {
def main(args: Array[String]): Unit = {
// 当做接口使用的特质
val logger = new ConsoleLogger
logger.log("console log message...")
// 带有具体实现的特质
val accountAction = new AccountAction
accountAction.withdraw(1000)
}
}
带有特质的对象
在构造单个对象时,可以为它添加特质
在定义子类时可以使用不做任何实现的特质,在构造具体对象的时候混入一个更合适的实现
特质中重写抽象方法,必须在方法上使用 abstract 及 override
// 有默认实现,但是什么也没有做
trait Logged {
def log(msg: String) {}
}
trait FileLogged extends Logged {
override def log(msg: String): Unit = println("saving file : " + msg)
}
class AccountAction extends Account with Logged {
def withdraw(amount: Double): Unit = {
if (amount > nowBalance) {
log("insufficient funds")
} else {
log("enough funds")
}
}
}
object TraitLearn {
def main(args: Array[String]): Unit = {
// 带有特质的对象,可以混入不同的日志
val accountActionLogger = new AccountAction with FileLogged
accountActionLogger.withdraw(1000)
}
}
// 运行输出结果
saving file : insufficient funds
叠加在一起的特质
可以为类或对象添加多个互相调用的特质,从最后一个开始被处理
// 为日志增加时间戳
trait TimestampLogged extends Logged {
override def log(msg: String): Unit = super.log(new java.util.Date() + " " + msg)
}
// 如果日志内容长度超过10,截断
trait ShortLogged extends Logged {
override def log(msg: String): Unit = super.log(if (msg.length <= 10) msg else msg.substring(0, 10) + "...")
}
object TraitLearn {
def main(args: Array[String]): Unit = {
// 带有特质的对象
val accountActionLogger = new AccountAction with FileLogged with TimestampLogged with ShortLogged
accountActionLogger.withdraw(1000)
val accountActionLogger1 = new AccountAction with FileLogged with ShortLogged with TimestampLogged
accountActionLogger1.withdraw(1000)
}
}
// 输出结果为
saving file : Sat Jan 06 12:38:07 CST 2018 insufficie...
saving file : Sat Jan 06...
注意上面的特质调用顺序及log方法每一个都将修改过的消息传递给supper.log
特质构造顺序
和类一样,特质也可以有构造器,由字段的初始化和其他特质体中的语句构成
构造器执行顺序
首先调用超类的构造器
特质构造器在超类构造器之后、类构造器之前执行
特质由左到右被构造
每个物质当中,父特质先被构造
如果多个特质共有一个父特质,而那个父特质已经被构造,则不会再次构造
所有的特质构造完毕,子类被构造
示例
class AccountAction extends Account with FileLogged with ShortLogged {
...
}
构造器执行顺序如下
超类 Account
Logged ,第一个特质的父特质
FileLogged 第一个特质
ShortLogged 第二个特质,它的父特质Logged已被构造
AccountAction 子类