第1题
扩展如下的 BankAccount 类,新类 ChekingAccount 对每次存款和取款都收取1美元的手续费。
1 2 3 4 5 6 class BankAccount (InitialBalance : Double ) { private var balance = initialBalance def deposit (amount: Double ) = {balance += amount; balance} def withdraw (amount: Double ) = {balance += amount; balance} def currentBalance = balance }
编码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package excercises.chapter8class CheckingAccount (InitialBalance : Double ) extends BankAccount (InitialBalance : Double ) { override def deposit (amount: Double ): Double = { super .deposit(amount - 1 ) } override def withdraw (amount: Double ): Double = { super .withdraw(amount + 1 ) } override def currentBalance : Double = super .currentBalance } object CheckingAccount extends App { val checkingAccount = new CheckingAccount (2000 ) val deposit = 1000 val withdraw = 800 checkingAccount.deposit(deposit) println("存入 :" + deposit + "余额: " + checkingAccount.currentBalance) checkingAccount.withdraw(withdraw) println("取出 :" + withdraw + "余额: " + checkingAccount.currentBalance) }
运行结果如下:
1 2 3 4 [info] Running excercises.chapter8.CheckingAccount 存入 :1000 余额: 2999.0 取出 :800 余额: 3800.0 [success] Total time: 3 s, completed Apr 27 , 2019 3 :15 :56 PM
第2题
扩展前一个练习的 BankAccount类,新类 SavinigsAccount ,每个月有利息产生(earnMonthlyInterest 方法被调用),并且有每月三次免手续费的存款或取款。在 earnMonthlyInterest方法中重置交易次数。
新类代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package excercises.chapter8class SavingsAccount (InitialBalance : Double ) extends BankAccount (InitialBalance ) { private var freeTradeCount = 3 private val interstRate = 0.01 def currentCount = freeTradeCount override def deposit (amount: Double ): Double = { if (freeTradeCount > 0 ){ freeTradeCount -= 1 super .deposit(amount) } else super .deposit(amount - 1 ) } override def withdraw (amount: Double ): Double = { if (freeTradeCount > 0 ){ freeTradeCount -= 1 super .withdraw(amount) } else super .withdraw(amount + 1 ) } def earnMonthlyInterest (): Double = { freeTradeCount = 3 super .deposit(super .deposit(0 ) * interstRate) super .deposit(0 ) * interstRate } } object saveingsAccount extends App { private val depBal: Double = 1000 private val witBal: Double = 200 private var interest: Double = 0.0 private val saveingsaccount: SavingsAccount = new SavingsAccount (500 ) for (i <- 1 to 32 ){ if (i >= 1 && i <= 5 ){ saveingsaccount.deposit(depBal) println(i + "号存入: " + depBal + ",余额: " + saveingsaccount.currentBalance + ",剩余免费次数: " + saveingsaccount.currentCount) } else if (i >= 29 && i <= 31 ){ if (i == 30 ){ interest = saveingsaccount.earnMonthlyInterest() saveingsaccount.deposit(interest) saveingsaccount.currentCount } saveingsaccount.withdraw(witBal) println(i + "号取出: " + witBal + ",余额: " + saveingsaccount.currentBalance + ",剩余免费次数: " + saveingsaccount.currentCount) } } println("一个月产生的利息:" + interest + ",剩余免费次数:" + saveingsaccount.currentCount) }
测试结果如下:
1 2 3 4 5 6 7 8 9 10 [info] Running excercises.chapter8.saveingsAccount 1 号存入: 1000.0 ,余额: 1500.0 ,剩余免费次数: 2 2 号存入: 1000.0 ,余额: 2500.0 ,剩余免费次数: 1 3 号存入: 1000.0 ,余额: 3500.0 ,剩余免费次数: 0 4 号存入: 1000.0 ,余额: 4499.0 ,剩余免费次数: 0 5 号存入: 1000.0 ,余额: 5498.0 ,剩余免费次数: 0 29 号取出: 200.0 ,余额: 5699.0 ,剩余免费次数: 0 30 号取出: 200.0 ,余额: 6013.5499 ,剩余免费次数: 1 31 号取出: 200.0 ,余额: 6213.5499 ,剩余免费次数: 0 一个月产生的利息:57.5599 ,剩余免费次数:0
第3题
翻开你喜欢的 Java 或 C++教科书,一定会找到用来讲解继承层级的示例,可能是员工、宠物、图形或类似的东西。用 Scala 来实现这个示例。
这里找到了Daniel Liang的《Introduction to Java Comprehension Version》中的 Listing 11.1 和11.2,代码如下:
Listing 11.1 SimpleGeometricObject.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package excercises.chapter8;public class SimpleGeometricObject { private String color = "white" ; private boolean filled; private java.util.Date dateCreated; public SimpleGeometricObject () { dateCreated = new java .util.Date(); } public SimpleGeometricObject (String color, boolean filled) { dateCreated = new java .util.Date(); this .color = color; this .filled = filled; } public String getColor () { return color; } public void setColor (String color) { this .color = color; } public boolean isFilled () { return filled; } public void setFilled (boolean filled) { this .filled = filled; } public java.util.Date getDateCreated () { return dateCreated; } public String toString () { return "created on " + dateCreated + "\ncolor: " + color + " and filled: " + filled; } }
Listing 11.2 CircleFromSimpleGeometricObject.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package excercises.chapter8;public class CircleFromSimpleGeometricObject extends SimpleGeometricObject { private double radius; public CircleFromSimpleGeometricObject () { } public CircleFromSimpleGeometricObject (double radius) { this .radius = radius; } public CircleFromSimpleGeometricObject (double radius, String color, boolean filled) { this .radius = radius; setColor(color); setFilled(filled); } public double getRadius () { return radius; } public void setRadius (double radius) { this .radius = radius; } public double getArea () { return radius * radius * Math.PI; } public double getDiameter () { return 2 * radius; } public double getPerimeter () { return 2 * radius * Math.PI; } public void printCircle () { System.out.println("The circle is created " + getDateCreated() + " and the radius is " + radius); } }
如果用 scala,则实现如下:
1 2 3 4 5 6 7 8 9 10 11 package excercises.chapter8class Geometric (color: String , filled: Boolean , dateCreated: java.util.Date ) { override def toString : String = "created on " + dateCreated + "\ncolor: " + color + " and filled: " + filled } class Circle (color: String , filled: Boolean , dateCreated: java.util.Date , radius: Double ) extends Geometric (color, filled, dateCreated ) { def printCircle (): Unit = { println("The circle is created " + dateCreated + " and the radius is " + radius) } }
第4题
定义一个抽象类 Item,加入方法 price 和 description。SimpleItem 是一个在构造器中给出价格和描述的物件。利用 val 可以重写 def 这个事实。Bundle 是一个可以包含其他物件的物件。其价格是打包中所有物件的价格之和。同时提供一个将物件添加到打包当中的机制。以及一个合适的 description 方法。
根据题意,编码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package excercises.chapter8abstract class Item { def price : Double def description : String } class SimpleItem (override val price: Double , override val description: String ) extends Item { } class Bundle ( ) extends Item { val itemList = scala.collection.mutable.ArrayBuffer [Item ]() def addItem (item: Item ): Unit ={ itemList += item } override def price : Double = { var itemPrice: Double = 0.0 itemList.foreach(i => itemPrice += itemPrice + i.price) itemPrice } override def description : String = { var itemDescription = "" itemList.foreach(i => itemDescription = itemDescription + "," + i.description) itemDescription } } object ItemTest extends App { val bundle = new Bundle val priceArr = Array (4299 , 6888 , 8999 , 20888 ) val descriptionArr = Array ("Ipad" , "Iphone XS" , "MacBook" , "MacBook Pro" ) for (i <- 0 until 4 ){ bundle.addItem(new SimpleItem (priceArr(i), descriptionArr(i))) } println("购物车信息如下:" ) bundle.itemList.foreach( item => println("描述:" + item.description + ", " + "价格:" + item.price)) println("所购物品清单如下:" + bundle.description) println("本次消费合计:" + bundle.price + " ¥" ) }
测试运行结果如下:
1 2 3 4 5 6 7 8 9 [info] Running excercises.chapter8.ItemTest 购物车信息如下: 描述:Ipad , 价格:4299.0 描述:Iphone XS , 价格:6888.0 描述:MacBook , 价格:8999.0 描述:MacBook Pro , 价格:20888.0 所购物品清单如下:,Ipad ,Iphone XS ,MacBook ,MacBook Pro 本次消费合计:100830.0 ¥ [success] Total time: 7 s, completed Apr 28 , 2019 3 :11 :14 PM
第5题
设计一个 Point 类,其 x 和 y 坐标可以通过构造器提供。提供一个子类 LabeledPoint,其构造器接受一个标签值和 x、y 坐标,比如:
1 new LabeledPoint ("Black Thursday" , 1929 , 230.07 )
根据题意编码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package excercises.chapter8class Point (val x: Double , val y: Double ) { override def toString : String = "x坐标为:" + x + ", " + "y坐标为:" + y } class LabeledPoint (val label: String , override val x: Double , override val y: Double ) extends Point (x, y ) { override def toString : String = "label = " + label + ", " + super .toString } object PointTest extends App { val point = new Point (3 , 4 ) val labeledPoint = new LabeledPoint ("*" , 3 , 4 ) println(point) println(labeledPoint) }
测试结果如下:
1 2 3 4 [info] Running excercises.chapter8.PointTest x坐标为:3.0 , y坐标为:4.0 label = *, x坐标为:3.0 , y坐标为:4.0 [success] Total time: 3 s, completed Apr 28 , 2019 3 :33 :00 PM
第6题
定义一个抽象类 Shape、一个抽象方法 centerPoint,以及该抽象类的子类 Rectangle 和 Circle。为子类提供合适的构造器,并重写 centerPoint 方法。
根据题意编码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package excercises.chapter8abstract class Shape { def centerPoint : Point } class Rectangle (val p1: Point , val p2: Point , val p3: Point , val p4: Point ) extends Shape { override def centerPoint : Point = { var x: Double = 0.0 var y: Double = 0.0 x = (p1.x + p2.x + p3.x +p4.x) / 2 y = (p1.y + p2.y + p3.y +p4.y) / 2 val centerpoint = new Point (x, y) centerpoint } } class GeoCircle (val cp: Point , val radius: Double ) extends Shape { override def centerPoint : Point = { cp } } object ShapeTest extends App { val p1 = new Point (1 , 5 ) val p3 = new Point (1 , 15 ) val p2 = new Point (10 , 5 ) val p4 = new Point (10 , 15 ) val rect = new Rectangle (p1, p2, p3, p4) println("rect的中心点坐标是:" + rect.centerPoint.toString) val circle = new GeoCircle (p4, 15 ) println("circle的中心点坐标是:" + p4.toString) }
测试运行结果如下:
1 2 3 4 [info] Running excercises.chapter8.ShapeTest rect的中心点坐标是:x坐标为:11.0 , y坐标为:20.0 circle的中心点坐标是:x坐标为:10.0 , y坐标为:15.0 [success] Total time: 20 s, completed Apr 28 , 2019 4 :14 :18 PM
第7题
提供一个 Square 类,扩展自 java.awt.Rectangle并且有三个构造器:一个以给定的端点和宽带构造正方形,一个以(0, 0)为端点和给定的宽度构造正方形,一个以(0, 0)为端点、0为宽度构造正方形。
根据题意编码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package excercises.chapter8import java.awt.{Point => JavaPoint }import java.awt.{Rectangle => JavaRectangle }class Square extends JavaRectangle { this .height = 0 this .width = 0 this .x = 0 this .y = 0 def this (p: JavaPoint , w: Int ){ this () this .height = w this .width = w this .x = p.x this .y = p.y } def this (width: Int ){ this (new JavaPoint (0 , 0 ), width) } } object SqureTest extends App { val rect1 = new Square () val rect2 = new Square (15 ) val rect3 = new Square (new JavaPoint (2 , 3 ), 25 ) println(rect1) println(rect2) println(rect3) }
测试运行结果如下:
1 2 3 4 5 6 [info] Running excercises.chapter8.SqureTest excercises.chapter8.Square [x=0 ,y=0 ,width=0 ,height=0 ] excercises.chapter8.Square [x=0 ,y=0 ,width=15 ,height=15 ] excercises.chapter8.Square [x=2 ,y=3 ,width=25 ,height=25 ] [success] Total time: 4 s, completed Apr 28 , 2019 5 :26 :57 PM
第8题
编译8.6节中的 Person 和 SecretAgent 类并使用 javap 分析类文件。总共多少 name 的 getter 方法?他们分别取什么值?(提示:可以用-c 和-private 选项)
原程序代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package excercises.chapter8class Person ( val name: String ) { override def toString=getClass .getName + "name= " + name + "]" } class SecretAgent (codename: String ) extends Person (codename ) { override val name = "secret" override val toString = "secret" }
将代码编译以后用 javap -p 查看结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ⇒ javap -p excercises.chapter8.Person Compiled from "Person.scala" public class excercises .chapter8 .Person { private final java.lang.String name; public java.lang.String name(); public java.lang.String toString(); public excercises.chapter8.Person (java.lang.String ); } ⇒ javap -p excercises.chapter8.SecretAgent Compiled from "Person.scala" public class excercises .chapter8 .SecretAgent extends excercises .chapter8 .Person { private final java.lang.String name; private final java.lang.String toString; public java.lang.String name(); public java.lang.String toString(); public excercises.chapter8.SecretAgent (java.lang.String ); }
很明显,两个类中都有 name()方法,但是子类的方法重载了父类的方法。
第9题
在8.10节的 Creature 类中,将 val range 替换成一个 def。如果你在 Ant 子类中也用 def 的话会有什么效果?如果在子类中使用 val 又会有什么效果?为什么?
def覆写def,子类的env可以正确初始化。而用val覆写def,env会被初始化成0长度。这个跟val覆写val的道理是一样的。父类和子类同时存在私有的同名变量range和相同的range的getter,但是父类构造函数先被调用,却在其中调用子类的getter。因为父类 的getter已被子类覆写。子类的range因为此时还没初始化,所以返回了0,而且因为是 val变量,一旦被初始化以后,子类的 range 无法继续修改。父类构造函数,错误地使用0来初始化了env。这种行为本身就是个坑,但是也提供了非常大的灵活性。面向对象的Template设计模式就依赖这种行为实现的,所以还是多多善用为妙。
回答参考以下链接:
Scala学习(八)练习
第10题
文件scala/collection/immutable/Stack.scala包含如下定义:
1 class Stack [A ] protected (protected val elems: List [A ] )
请解释 protected 关键字的含义。(提示:回顾我们在第5章关于私有构造器的讨论)
前一个 protected 是指主构造器的权限, 即默认情况下,是不能以传入 elems 的方式创建 Stack 对象的,只能通过辅助构造器来构造 Stack 对象。elems的 protected 指的是这个参数只有该类的子类才能访问,这个访问的范围与 Java 中的有些不同,Java 的 protected 关键字除了子类,类本身以及同一个包内都可以访问。