快学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 ...
随机推荐
- stb_image读取图片数据
#include <iostream> #include <fstream> #include <stbi/stb_image.h> const unsigned ...
- Shader之ECEF——LLH
uniform mat4 osg_ViewMatrix; uniform mat4 osg_ViewMatrixInverse; uniform mat4 osg_ModeViewMatrix; un ...
- MVC5 Entity Framework学习之创建复杂的数据模型
目录(?)[-] 使用属性来自定义数据模型 DataType属性 StringLength属性 Column 属性 完成对Student实体的更改 Required 属性 Display 属性 Ful ...
- 离线消息如何实现?-- ESFramework 4.0 快速上手(02)
在ESFramework 4.0 快速上手一文中,主要介绍了如何使用ESPlus.Rapid命名空间中的引擎来快速地构建基于TCP的网络通信系统,即使是使用ESPlus.Rapid来进行ESFrame ...
- hdu 5874 Friends and Enemies icpc大连站网络赛 1007 数学
#include<stdio.h> #include<iostream> #include<algorithm> #include<math.h> #i ...
- Gentoo网络配置
网卡识别配置 要开始配置你的网卡,你首先需要告诉Gentoo RC系统你的网卡. 可以用ifconfig命令查看自己网卡名字: ifconfig -a 网卡名字(如eth0)的识别是通过在/etc/i ...
- VS2010编译错误 LNK 2019 unresolved external symbol错误解决办法
Link错误有很多种,主要是没有在连接中加入lib文件路径,或者lib配置正确,传参错误 一个solution里面多个project之间引用其他project函数会出现这个错误,由于包含了头文件而没处 ...
- javascript中onSubmit="return xxx()"的问题
javascript中onSubmit="return xxx()"刚开始我是想不通为什么要加return在里面呢,后来想想onSubmit="flase"就不 ...
- Ajax.BeginForm 异步上传附件 替代方案
一:问题描述 含有文件信息表单内容,想通过异步上传到服务器,但是使用Ajax.BeginForm上传时,后台无法获取文件信息 二:解决方案 通过 $.ajaxFileUpload 可以实现文件及 ...
- javascript 深入浅出 (未完成4-17)
慕课网javascript总结 课程地址 课程大纲: 一.数据类型 二.表达式和运算符 三.语句 四.对象 五.数组 六.函数 七.this 八.闭包和作用域 九.OOP 十.正则与模式匹配 ---- ...