Ch05 类 - 练习

时间:2023-03-10 01:51:30
Ch05 类 - 练习

1. 改进5.1节的Counter类,让它不要在Int.MaxValue是变成负数。

class Counter{
    private var value = Int.MaxValue
    def increment() { value = if ( value < Int.MaxValue) value + 1 else value }
    def current = value
}
val myCounter = new Counter()
myCounter.increment()
println(myCounter.current)
/*result
2147483647
*/

2.  编写一个BankAccount类,加入deposit和withdraw方法,和一个只读的balance属性。
class BankAccount(val balance:Double=0.0){
    def deposit() {}
    def withdraw() {}
}
val obj = new BankAccount(100.00)
println(obj.balance)
/*result
100.0
*/

3. 编写一个Time类,加入只读属性hours和minutes,和一个检查某一时刻是否早于另一时刻的方法before(other: Time): Boolean。
Time对象应该以new Time(hrs, min)方式构建,其中hrs小时数以军用时间格式呈现(介于0和23之间)。

class Time(val hours:Int, val minutes:Int){
    def before(other:Time):Boolean={
        if(hours == other.hours) minutes < other.minutes
        else hours < other.hours
    }
}
val = new Time(9,0)
val = new Time(9,0)
val = new Time(9,30)
val = new Time(10,0)
println(a.before(b))
println(a.before(c))
println(a.before(d))
/*result
false
true
true
*/


4. 重新实现前一个练习中的Time类,将内部呈现改成自午夜起的分钟数(介于0到24x60-1之间)。不要改变公有接口。也就是说,客户端代码不应因你的修改而受到影响。

class Time(val hours:Int, val minutes:Int){
    private val timeMinutes = hours * 60 + minutes
     
    def before(other:Time):Boolean={
        timeMinutes < other.timeMinutes
    }
}
val = new Time(9,0)
val = new Time(9,0)
val = new Time(9,30)
val = new Time(10,0)
println(a.before(b))
println(a.before(c))
println(a.before(d))
/*result
false
true
true
*/

5. 创建一个Student类,加入可读写的JavaBeans属性name(类型为String)和id(类型为Long)。有哪些方法被生成?(用javap查看。)
你可以在Scala中调用JavaBeans版的getter和setter方法吗?应该这样做吗?
回答:有以下方法被生成:
  public java.lang.String name();
  public void name_$eq(java.lang.String);

public java.lang.String getName();
  public void setName(java.lang.String);

  public long id();
  public void id_$eq(long);

  public long getId();
  public void setId(long);
  

在Scala中调用JavaBean版的getter和setter是可以的。除非是为了工具的兼容性,不推荐这样做。因为这样做破坏了Scala的编程风格。


import scala.beans.BeanProperty
class Student{
    @BeanProperty var name: String = _
    @BeanProperty var id: Long = _
}
val = new Student
a.name = "Jonathan"
a.id = 43344506L
printf("%s's id is %d\n", a.name, a.id)
a.setName("Frank")
a.setId(43344599L)
printf("%s's id is %d\n", a.getName(), a.getId())
/*result
G:\share\scala>scalac e5-5.scala
G:\share\scala>javap Student.class
Compiled from "e5-5.scala"
public class Student {
  public java.lang.String name();
  public void name_$eq(java.lang.String);
  public void setName(java.lang.String);
  public long id();
  public void id_$eq(long);
  public void setId(long);
  public java.lang.String getName();
  public long getId();
  public Student();
}
G:\share\scala>scala e5-5.scala
Jonathan's id is 43344506
Frank's id is 43344599
*/

6. 在5.2节的Person类中提供一个主构造器,将负年龄转换为0。
class Person(var age:Int){
    if(age<0) age = 0
}
val = new Person(-1)
printf("The person's age is %d\n", a.age)
/*result
The person's age is 0
*/

7. 编写一个Person类,其主构造器接受一个字符串,该字符串包含名字、空格和姓,如new Person("Fred Smith")。
提供只读属性firstName和lastName。主构造器的参数应该是var、val还是普通参数呢?为什么?

语法上都没有错,但从逻辑上看,应该使用val。如果为var,则对应的此字符串有getter和setter方法,而Person中的firstName和lastName为只读的,
所以不能重复赋值。

class Person(val name:String){
    val firstName = name.split(" ")(0)
    val lastName = name.split(" ")(1)
}
val = new Person("Jonathan Chen")
printf("The person's lastName is %s\n", a.lastName)
printf("The person's firstName is %s\n", a.firstName)
/*result
The person's lastName is Chen
The person's firstName is Jonathan
*/

8. 创建一个Car类,以只读属性对应制造商、型号名称、型号年份以及一个可读写的属性用于车牌。提供四组构造器。
每一个构造器都要求制造商和型号名称为必填。型号年份以及车牌为可选,如果未填,则型号年份设置为-1,车牌设置为空字符串。
你会选择哪一个作为你的主构造器?为什么?
选择 Car(val maker:String, val typeName:String, val year:Int, var id:String) 为主构造器,因为所有的辅助构造器都可以引用主构造器。
class Car(val maker:String, val typeName:String, val year:Int, var id:String){
    def this(maker:String, typeName:String){
        this(maker, typeName, -1"")
    }
    def this(maker:String, typeName:String, year:Int){
        this(maker, typeName, year, "")
    }
    def this(maker:String, typeName:String, id:String){
        this(maker, typeName, -1, id)
    }
    override def toString = "Maker:%s, TypeName:%s, Year:%d, Id:%s".format(maker, typeName, year, id)
}
val = new Car("BMW","A6")
val = new Car("BMW","A6",2015,"TheOne")
val = new Car("BMW","A6",2015)
val = new Car("BMW","A6","TheOne")
println(a)
println(b)
println(c)
println(d)
/*result
Maker:BMW, TypeName:A6, Year:-1, Id:
Maker:BMW, TypeName:A6, Year:2015, Id:TheOne
Maker:BMW, TypeName:A6, Year:2015, Id:
Maker:BMW, TypeName:A6, Year:-1, Id:TheOne
*/


9. 在Java、C#或C++(你自己选)重做前一个练习。Scala相比之下精简多少?
略。本书P60页对此有所比较。

10.  考虑如下类:
class Employee(val name:String, var salary:Double){
    def this() { this("Join Q. Public", 0.0) }
}
重写该类,使用显式的字段定义,和一个缺省主构造器。你更倾向于使用哪一种形式?为什么?
这两种写法并不完全等价。第一种写法有两组构造器,而第二种写法只有默认的主构造器。

class Employee{
    val name:String = "Join Q. Public"
    var salary:Double = 0.0
}
val = new Employee
/*result
*/