Scala中子类继承父类与JAVA一样,也使用extends关键字。继承的概念两种语言基本是一致的,都是表示子类可以从父类继承其属性与方法。子类可以派生自有的属性和方法,通过继承实现代码复用。此外,子类也可以覆盖父类的属性和方法。与JAVA中类似,如果某个类使用final修饰,或者属性或方法用final修饰,则该类无法被继承的,相应的属性与方法也是无法覆盖的。
class Sprint {
private var name = "Sprint01"
def getName = name
}
class SubSprint extends Sprint {
private var point = "8"
def getPoint = point
}
实例化SubSprint对象并调用对应get方法:
var subSprint = new SubSprint
subSprint.getName // 返回res0: String = Sprint01
subSprint.getPoint // 返回res1: String = 8
override关键字和super方法
Scala中子类可以通过override关键字来覆盖父类中的方法。同样,还可以在子类中使用super方法来显式调用父类方法。这两个关键字对应的处理逻辑与JAVA基本一致,对于JAVA开发者基本可以认为是等同的。下面是一个例子:
class Order {
private var id = "901077890"
def getId = id
}
//定义OrderDelivery类继承Order
class OrderDelivery extends Order {
private var prefix = "DE_"
private var address = "Shanghai Yangpu Rd.Yingkou No.1033 B1"
def getAddress = address
//需要使用override关键字来覆盖父类的getId方法
override def getId = prefix + super.getId
}
var od = new OrderDelivery
println(od.getId) // 返回DE_901077890
isInstanceOf和asInstanceOf方法
Scala中与JAVA类似,也有转型的概念,可以使用isInstanceOf判断对象是否指定类型的对象,并可以使用asInstanceOf将对象转换为指定类型。如果对象是null则isInstanceOf会返回false,asInstanceOf也会返回null值。如果对象为null值且未调用isInstanceOf判断,而直接使用asInstanceOf进行转换,此时会抛出异常。
class UserData
class SMS extends UserData
val d: UserData = new SMS
var s: SMS = null
println("d<UserData>.isInstanceOf[SMS]="+ d.isInstanceOf[SMS])
// 返回:d<UserData>.isInstanceOf[SMS]=true
if (d.isInstanceOf[SMS]) s =d.asInstanceOf[SMS]
println("s<SMS>.isInstanceOf[SMS]="+ s.isInstanceOf[SMS])
// 返回:s<SMS>.isInstanceOf[SMS]=true
println("s<SMS>.isInstanceOf[UserData]="+ s.isInstanceOf[UserData])
// 返回:s<SMS>.isInstanceOf[UserData]=true
getClass和classOf方法
Scala中的isInstanceOf方法只能判断对象是否某个类型,但不能精确判断对象就是指定类的对象。如果某些场景下需要精确判断对象是否派生自指定类,可以使用getClass和classOf方法实现。示例如下:
val d: UserData = new SMS
var s = d.asInstanceOf[SMS]
d.isInstanceOf[UserData] // Boolean = true
d.isInstanceOf[SMS] // Boolean = true
s.getClass == classOf[UserData] // Boolean= false
s.getClass == classOf[SMS] // Boolean =true
模式匹配与类型判断
模式匹配通过精简的方式来实现类型判断,功能上与isInstanceOf一致。
d match {
case e: SMS => println("d match SMS")
case e: UserData => println("d match UserData")
case _ =>println("unknown type")
}
protected修饰符
Scala中的protected修饰符含义与JAVA一致,用来修饰属性和方法,表示对应资源可由子类访问。还可以用protected[this]来限制只能在当前子类对象中访问父类的属性与方法,而不允许调用其它传入的子类对象中父类的资源。
class Msg(id: String) {
protected[this] var secretKey: String = id + "SECRET@9Y6"
}
class VoiceMsg(id: String) extends Msg(id:String) {
def localSecret = println("SecretKey of current instance: " +secretKey)
def remoteSecret(v: VoiceMsg) {
println("local secret: " + secretKey)
println(" remote secret: " + v.secretKey)
}
}
编译时会报以下错误:
Error:(9, 40) value secretKey is not amember of VoiceMsg
println(" remote secret: " +v.secretKey)
调用父类构造器
Scala中每个类可以有一个主构造器和任意多个辅助构造器,而每个辅助构造器第一行都必须是调用其他辅助构造器或主构造器;因此子类的辅助构造器中无法直接调用父类构造器。如果在某些设计模式中需要直接调用,可以采用如下语法:
class Order(val userId: String, valorderId: Int)
// userId和orderId不需要加var或val修饰,通过主构造器直接调用父类构造器
class Item(userId: String, orderId: Int,var price: Double) extends Order(userId, orderId) {
def this(userId: String) {
this(userId, 0, 0.00)
}
def this(orderId: Int) {
this("USR9008X10", orderId, 0.00)
}
}
即只能在子类主构造器中调用父类构造器。此外不能用val或var修饰子类的主构造器入参,否则就变成override了。
匿名内部类
Scala中的匿名内部类机制与JAVA类似,很常见且应用很广。参考以下示例:
// 定义抽象类Producer
abstract class Producer(protected valtopic: String) {
def send: String = "AbstractProducer send msg with topic " +topic
}
// 创建Producer类型的匿名类
var mp = new Producer("phone") {
override def send : String = "AnonymousProducer send msg with topic" + topic
}
// 定义方法,入参为匿名类实例
def sendMsg(p : Producer): Unit = {
println(p.send)
}
sendMsg(mp) // 返回AnonymousProducer send msg with topic phone
抽象类
与JAVA中概念类似,进行系统设计时可以先定义抽象类及对应方法签名,由具体实现时的子类型来覆盖,这些仅有方法签名不含实现的就是抽象方法。而一个类中如果包含象方法,那么声明类时需要用abstract关键字,而且抽象类是不可实例化的。此外在子类中覆盖抽象父类中的抽象方法时,不需要使用override关键字。
// 定义抽象类Producer
abstract class Producer(protected valtopic: String) {
def send: String = "AbstractProducer send msg with topic " +topic
}
// 对抽象类进行实现并覆盖抽象方法
class KafkaProducer(topic : String)extends Producer(topic) {
override def send : String = "KafkaProducer send msg with topic" + topic
}
// 对KafkaProducer实例化
var kp = newKafkaProducer("Logger")
kp.send // 返回KafkaProducer send msg with topic Logger
抽象属性
如果父类中定义属性,但没有给出初始值,则认为该属性为抽象属性。这个特性是Scala中比较特殊的,与JAVA差异较大,JAVA中的抽象大多情况下仅针对方法级别。抽象属性意味着scala会根据默认规则,对var或val类型的属性生成对应的getter和setter方法,但父类中是没有该属性的,因此子类必须定义自己的具体属性,并覆盖抽象属性,此时不需要使用override关键字。
// 定义抽象类Producer
abstract class Producer(protected valtopic: String) {
val startIdx: Integer
}
// 对抽象类进行实现并覆盖抽象方法
class KafkaProducer(topic : String) extendsProducer(topic) {
val startIdx : Integer = 1000
}
// 对KafkaProducer实例化
var kp = newKafkaProducer("Logger")
kp.startIdx // res1: Integer = 1000