快学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章关于私有构造器的讨论。)

此构造方法只能被其子类来调用,而不能被外界直接调用。

时间: 2024-08-12 09:58:10

快学Scala-第八章 继承的相关文章

快学Scala 第十九课 (trait的abstract override使用)

trait的abstract override使用: 当我看到abstract override介绍的时候也是一脸懵逼,因为快学scala,只介绍了因为TimestampLogger中调用的super.log依旧是个abstract class,所以必须在方法前加上abstract和override.但是并没有具体介绍如何使用,然后查阅了其他文档,才明白使用方法. 下面的代码定义了超类LoggerEmpty,这个定义意味着该特质只能混入扩展LoggerEmpty的类中. 在特质中声明抽象方法中有

快学scala笔记.

第一章 基础 val 定义的值实际上是一个常量 var 声明其值可变的变量 val xmax,ymax = 100 var greeting,message: String = null 1.3 常用类型 Scala的7种数值类型:Byte.Char.Short.Int.Long.Float和Double 1.toString() 2.to(10) "Hello".intersect("World") 1.4 算术和操作符重载 val answer = 8 * 5

快学Scala课后习题答案

分享一个之前做快学Scala的课后习题(2-21章节,19无)的Github链接,我把习题的文字写在了每个回答的注释上面,这样方便大家对照着看,省的回过头去对照着pdf看了,如果有做的不对的地方希望大家给予指正. 链接如下,http://github.com/fxxkinglife/scala-hello. 举一个第二章节的例子, object charpter02 { /* * 2.1 * 一个数字如果为正数,则它的signum为1; * 如果是负数,则signum为-1; * 如果为0,则s

快学scala 第十一章 操作符 读书笔记及习题答案代码

chapter 11 操作符 标签:快学scala 一.笔记 scala种可以在反引号中包含几乎任何字符序列, val 'val' = 42 所有的操作符都是左结合的,除了以冒号(:)结尾的操作符,和赋值操作符.用于构造列表的::操作符是又结合的.1::2::Ni1的意思是1::(2::Ni1),先创建出包含2的列表,这个列表又被作为尾巴拼接到以1作为头部的列表中. 2. 函数调用语法:f(arg1, arg2,...)扩展到可以应用于函数之外的值,如果f不是函数或方法,那么这个表达式等于f.a

快学Scala习题解答—第一章 基础

1 简介 近期对Scala比较感兴趣,买了本<快学Scala>,感觉不错.比<Programming Scala:Tackle Multi-Core Complexity on the Java Virtual Machine>好很多. 是本不错的入门书.而且每个章节都设置了难度级别,每章有习题,可以巩固Scala语法. 本文的目的就是针对这些习题进行解答 2 基础 2.1 在Scala REPL中键入3,然后按Tab键.有哪些方法可以被应用? 这个....直接操作一遍就有结果了.

快学Scala习题解答—第八章 继承

8 继承 8.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;

快学Scala 第十一课 (类继承)

类继承: class People { } class Emp extends People{ } 和Java一样,final的类不能被继承.final的字段和方法不能被override. 在Scala中重写一个非抽象方法必须使用override, 继承抽象方法前面加了override也没关系. abstract class Person { def say(s: String): Unit } class Worker extends Person{ override def say(s: S

快学Scala 第十八课 (trait多继承)

trait多继承: trait的继承并不像类拥有相同的含义!在下面这个例子中,如果还是运用类的继承的思想,那么运行结果将是什么也没有. trait Logged { def log(msg: String){ } } trait ConsoleLogger extends Logged { override def log(msg: String){ super.log(msg) } } 但是事实并非如此: trait Logged { def log(msg: String){ println

快学Scala 第九课 (伴生对象和枚举)

Scala没有静态方法和静态字段, 你可以用object这个语法结构来达到同样的目的. 对象的构造器只有在第一次被使用时才调用. 伴生对象apply方法: 类和它的伴生对象可以互相访问私有特性,他们必须存在于同一个源文件. 类中要访问类的伴生对象中成员,需要通过类.成员调用. class Account private (val id: Int, initialBalance: Double){ } object Account { def apply(initialBalance: Doubl

快学Scala 2

控制结构和函数 1.在Scala中,几乎所有构造出来的语法结构都有值.这个特性是为了使得程序更加精简,也更易读. (1)if表达式有值 (2)块也有值——是它最后一个表达式的值 (3)Scala的for循环就像是“增强版”的Java for循环 (4)分号(在绝大多数情况下)不是必须的 (5)void类型是Unit (6)避免在函数定义中使用return (7)注意别在函数式定义中漏掉了= (8)异常的工作方式和Java中基本一样,不同的是catch语句中使用“模式匹配” (9)Scala没有受