快学Scala-第八章 继承
知识点:
1.扩展类 extends关键字,在定义中给出子类需要而超类没有的字段和方法,或者重写超类的方法。
2.重写方法 在Scala中重写一个非抽象方法必须 override 修饰符
public class Person{
…….
override def toString = getClass.getName + “[name=”+name+”]”
}
override 修饰符可以在多个常见情况下给出有用的错误提示,包括:
1)当你拼错了要重写的方法名
2)当你不小心在新方法中使用了错误了参数类型
3)当你在超类中引入了新的方法,而这个新的方法与子类的方法相抵触(易违约基类问题的体现,超类的修改无法在不检查所有子类的前提下被验证)
在scala中调用超类的方法和Java完全一样,使用super关键字。
public class Employee extends Person{
……
override def toString = super.toString + “[salary=”+salary+ “]”
}
3. 类型检查和转换
要测试某个对象是否属于某个给定的类,可以用isInstanceOf方法,如果测试成功,可以用asInstanceOf方法将引用转换为子类的引用:
if(p.isInstanceOf[Employee]){
val s = p.asInstanceOf[Employee]//s的类型为Employee
……
}
如果p指向的是Employee类及其子类(比如Manager)的对象,则p.inInstanceOf[Employee]将会成功;如果p是null,则p.isInstanceOf[Employee]返回false,且p.asInstanceOf[Employee]将会成功;如果p不是一个Employee,则p.asInstanceOf[Employee]将抛出异常;如果想测试p指向的是一个Employee对象但又不是其子类的话,可以用 if(p.getClass == classOf[Employee]).classOf方法定义在scala.Predef对象中,会被自动引入。
4.受保护字段和方法 将字段或方法声明为protected,这样的成员可以被任何子类访问,但不能从其他位置看到,protected的成员对于类所属的包而言,是不可见的。Scala提供了一个protected[this]变体,将访问权限定在当前的对象。
5.类有一个主构造器和任意数量的辅助构造器,每个辅助构造器都必须以对先前定义的辅助构造器或主构造器的调用开始。子类的辅助构造器最终都会调用住构造器,只有主构造器可以调用超类的构造器。
6.重写字段
Scala字段由一个私有字段和取值器、改值器方法构成,你可以用另一个同名的val字段重写一个val(或不带参数的def)。
常见案例:val重写抽象的def。
abstract class Person {
def id: Int //每个人都有一个以某种方式计算出来的ID
……
}
class Student(override val id:Int) extends Person //学生ID通过构造器输入
用val | 用def | 用var | |
重写val | 子类有一个私有字段(与超类的字段名字相同——没问题) getter方法重写超类的getter的方法 |
错误 | 错误 |
重写def | 子类有一个私有字段 getter方法重写超类的方法 |
和JAva一样 | var可以重写getter/setter对。只重写getter会报错。 |
重写var | 错误 | 错误 | 仅当超类的var是抽象的才可以 |
def只能重写另一个def;val只能重写另一个val或不带参数的def;var只能重写另一个抽象的var。
7. 匿名子类
可以通过包含带有定义或重写的代码块的方式创建一个匿名的子类,比如:
val alien = new Person("Fred") {
def greeting = "Greetings,Earthling!My name is Fred."
}
将会创建一个结构类型的对象,该类型记为Person{def greeting:String},可以用这个类型作为参数类型的定义:
def meet(p:Person{def greeting:String}){
println(p.name + "says:" + p.greeting)
}
8.抽象类
和java一样,可以用abstract关键字来标记不能被实例化的类,通常是因为它的某个或某几个方法没有被完整定义。在Scala中,不想Java,不需要对抽象方法使用abstract关键字,只是省去其方法体,但和Java一样,如果某个类至少存在一个抽象方法,则该类必须声明为abstract。在子类中重写超类的抽象方法时,不需要使用override关键字。
9. 抽象字段
抽象字段就是一个没有初始值得字段。
abstract class Person{
val id: Int//带有抽象的getter方法的抽象字段
var name:String//带有抽象的getter和setter方法
}
具体的子类必须提供具体的字段,和方法一样,在子类中重写超类的抽象字段时,不需要override关键字。
10.对象相等性
在scala中,AnyRef的eq方法检查两个引用是否指向同一个对象,AnyRef的equals方法调用eq,当你实现类的时候,应该考虑重写equals方法。
如果定义class Item(val description: String, val price: Double), 你可能认为当两个物件有着相同描述和价格的时候是相等的。它的equals方法定义:
final override def equals(other :Any)={
val that = other.asInstanceOf[Item]
if (that == null) false
else description == that.description && price == that.price
}
将方法定义为final,是因为通常在子类中正确扩展相等性判断非常困难,问题出在对称性上,a.equals(b)和b.equals(a)的结果相同,尽管b属于a的子类。需要确保equals方法的参数类型为Any。
定义了equals是,还需要定义hashCode,在计算哈希码时,只应使用那些用来做相等性判断的字段。。
final override def hashcode = 13 * description.hahscode + 17 * price.hashCode
练习:参考网址
1.扩展如下的BankAccount类,新类CheckingAccount对每次存款和取款都收取1美元的手续费。
class BankAccount(initialBalance: Double){
private var balance = initialBalance
def deposit(amount: Double) = { balance += amount;balance}
def withdraw(amount: Double) = { balance –= amount; balance}
}
class CheckingAccount(initialBalance:Double) extends BankAccount(initialBalance){
override def deposit(amount:Double): Double = super.deposit(amount - 1)
override def withdraw(amount:Double): Double = super.withdraw(amount + 1)
}
2.扩展前一个练习的BankAccount类,新类SavingsAccount每个月都有利息产生(earnMonthlyInterest方法被调用),并且有每月三次免手续费的存款或取款。在earnMonthlyInterest方法中重置交易计数。
class SavingsAccount(initialBalance:Double) extends BankAccount(initialBalance){
private var num:Int = _
def earnMonthlyInterest()={
num = 3
super.deposit(1)
} override def deposit(amount:Double): Double = {
num -= 1
if(num<0) super.deposit(amount-1) else super.deposit(amount)
}
override def withdraw(amount:Double): Double = {
num -= 1
if(num<0) super.withdraw(amount + 1) else super.deposit(amount)
}
}
3.翻开你喜欢的Java或C++书籍,一定会找到用来讲解继承层级的示例,可能是员工、宠物、图形等,用Scala来实现这个示例。
//java
class Art{
Art(){System.out.println("Art constractor");}
} class Drawing extends Art{
Drawing(){System.out.println("Drawing constructor");}
}
public class Carton extends Drawing{
Carton(){Ststem.out.println("Carton costructor");}
}
//scala
class Art{
println("Art constructor")
}
class Drawing extends Art{
println("Drawing constructor")
}
class Carton extends Drawing{
println("Carton constructor")
}
4.定义一个抽象类Item,加入方法price和description,SimpleItem是一个在构造器中给出价格和描述的物件。利用val可以重写def这个事实。Bundle是一个可以包含其他物件的物件。其价格是打包中所有物件的价格之和。同时提供一个将物件添加到打包当中的机制,以及一个合适的description方法。
import collection.mutable.ArrayBuffer abstract class Item{
def price():Double
def description():String
override def toString():String = {
"description: " + description() + "price: " + price()
}
} class SimpleItem(val price:Double,val description:String) extends Item{ } class Bundle extends Item{
val items = new ArrayBuffer[Item]()
def addItem(item:Item){
items += item
}
def price():Double = {
var total = 0d
items.foreach(total += _.price())
total
} def description():String = {
items.mkString(" ")
}
}
5.设计一个Point类,其x和y坐标可以通过构造器提供,提供一个子类LabeledPoint,其构造器接受一个标签值和x\y坐标,比如:
new LabeledPoint(“Black Thurstry”,1929,230.07)
class Point(x:Int,y:Int){ }
class LabeledPoint(label:String,x:Int,y:Int) extends Point(x,y){ }
6.定义一个抽象类Shape,一个抽象方法centerPoint,以及该抽象类的子类Rentangle和Circle。为子类提供合适的构造器,并重写centerPoint方法。
abstract class Shape{
def centerPoint()
} class Rectangle(startX:Int,startY:Int,endX:Int,endY:Int) extends Shape{
def centerPoint(){}
} class Circle(x:Int,y:Int,radius:Double) extends Shape{
def centerPoint(){}
}
7.提供一个Square类,扩展自java.awt.Rectangle并且有三个构造器:一个以给定的端点和宽度构造正方形,一个以(0,0)为端点构造和给定的宽度构造正方形,一个以(0,0)为端点、0为宽度构造正方形。
import java.awt.{Point,Rectangle} class Square(point:Point,width:Int) extends Rectangle(point.x,point.y,width,width) {
def this(){
this(new Point(0,0),0)
}
def this(width:Int){
this(new Point(0,0),width)
}
}
8.编译8.6节中的Person和SecretAgent类并使用javap分析类文件。总共有多少name的getter方法?它们分别取什么值?(提示:可以用 –c 和 -private选项)
2,Person中取得的是传入的name,而SecretAgent取得的是默认的“secret”
9.在8.10节的Creature类中,将val range 替换成一个def。如果你在Ant子类中也用def的话会有什么效果?如果子类中使用val又会有什么效果?为什么?
在Ant中使用def没有问题,但是如果使用val则无法通过编译,因为val只能重写不带参数的def,这里的def是带参数的。
10.文件scala/collection/immutable/Stack.scala包含如下定义:
class Stack[A] protected(protected val elems: List[A])
请解释protected关键字的含义。(提示:回顾我们在第5章关于私有构造器的讨论。)
此构造方法只能被其子类来调用,而不能被外界直接调用。
快学Scala-第八章 继承的更多相关文章
- 《快学Scala》
Robert Peng's Blog - https://mr-dai.github.io/ <快学Scala>Intro与第1章 - https://mr-dai.github.io/S ...
- 快学Scala 第十九课 (trait的abstract override使用)
trait的abstract override使用: 当我看到abstract override介绍的时候也是一脸懵逼,因为快学scala,只介绍了因为TimestampLogger中调用的super ...
- 快学Scala习题解答—第一章 基础
1 简介 近期对Scala比较感兴趣,买了本<快学Scala>,感觉不错.比<Programming Scala:Tackle Multi-Core Complexity on th ...
- 快学Scala之特质
一个Scala类可以继承多个特质(trait), 特质可能会要求使用它们的类支持某个特定特性, 与Java接口不同, Scala特质可以给出这些特质的缺省实现. 要点如下: Scala中类只能继承一个 ...
- 《快学Scala》第八章 继承
- 快学Scala之继承
## 1. 继承 Scala语言通过 extends 关键字来继承类. 那么继承一个类有什么好处呢? 子类除了拥有继承自超类的方法和字段(即为val(常量), var(变量)所定义的), 还可 ...
- 快学Scala 第十一课 (类继承)
类继承: class People { } class Emp extends People{ } 和Java一样,final的类不能被继承.final的字段和方法不能被override. 在Scal ...
- 快学Scala 第十八课 (trait多继承)
trait多继承: trait的继承并不像类拥有相同的含义!在下面这个例子中,如果还是运用类的继承的思想,那么运行结果将是什么也没有. trait Logged { def log(msg: Stri ...
- [Scala] 快学Scala A1L1
基础 1.1 声明值和变量 在Scala中,鼓励使用val; 不需要给出值或变量的类型,这个信息可以从初始化表达式推断出来.在必要的时候,可以指定类型. 在Scala中,仅当同一行代码中存在多条语句时 ...
- 《快学Scala》——控制结构和函数
条件表达式 在Scala中if/else表达式有值,这个值就是跟在if或else之后的表达式的值.例如: if (x > 0) 1 else -1 上述表达式的值是1或-1,具体是哪一个取决于x ...
随机推荐
- ubuntu 操作系统相关操作
查看操作系统位数 命令: getconf LONG_BIT root@hbg:/# getconf LONG_BIT 64 查看操作系统信息 命令: lsb_release -a root@hbg: ...
- ffmpeg编译
CFLAGS=-g ./configure --enable-opengl --disable-yasm --enable-shared --enable-pic
- LeetCode OJ 42. Trapping Rain Water
Given n non-negative integers representing an elevation map where the width of each bar is 1, comput ...
- 移动端touch事件获取clientX, clientY
目有个交互需要实现手指滑动的交互,pc端使用mousedown,mousemove,mouseup监听实现. 但在ios设备上mousemove是不好监听的,同类的方法是touchstart,touc ...
- 水电pd建表
drop table ADMINSTER cascade constraints; /========================================================= ...
- apache与php安装
安装库文件 yum install -y pcre pcre-devel apr apr-deve 安装apache cd /usr/local/src/httpd-2.4.23 Bundled AP ...
- C++ 空类默认产生的类成员函数
C++的空类有哪些成员函数:. 缺省构造函数.. 缺省拷贝构造函数.. 缺省析构函数.. 缺省赋值运算符.. 缺省取址运算符.. 缺省取址运算符 const. 注意:有些书上只是简单的介绍了前 ...
- CentOS7 emacs安装
首先安装依赖库 依赖库: yum install gcc* yum install glib* yum install gtk* yum install ncurses* yum ...
- java代理的深入浅出(三)-JavaAssist,ASM
简介 类似字节码操作方法还有ASM.几种动态编程方法相比较,在性能上Javassist高于反射,但低于ASM,因为Javassist增加了一层抽象.在实现成本上Javassist和反射都很低,而ASM ...
- android常用工具类
import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkIn ...