开发者学堂课程【Scala 核心编程-基础:作业评讲】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/609/detail/8992
作业评讲
内容介绍
一、对象
二、继承
一、对象
1、编写一个 Conversions 对象,加入 inchesToCentimeters,gallonsToLiters 和 milesToKilometers 方法。
解析:本题比较简单,查找单位之间的换算,写一个 objet Conversions 对象,再定义三个方法就可解答。
答案:
object Conversions{
//英寸->厘米,英寸和厘米的单位转换是2.54。
def inchesToSantimeters(value:Double)=value * 2.54
//加仑->升,加仑和升单位转换是3.78541178。
def gallonsToLiters(value:Double)=value* 3.78541178
//英里->公里,英里和公里的单位转换是1.609344。
def milesToKilometers(value:Double)=value * 1.609344
}
2、定义一个 Point 类和一个伴生对象,使得可以不用 new 而直接用 Point(3,4)来构造 Point 实例 apply 方法的使用。
解析:构建对象有三种形式,第一种是 new,第二种是 apply,第三种是匿名子类。这里在 point 中加一个 apply 方法,apply 方法中可以接受两个指数,把 new隐藏在 apply 方法中。
答案:
//Point 类
class Point(x:Int = 0,y:Int=o) {
//在类中定义了两个属性,使用主构造器的方式初始化属性。
var x1=x
var y1=y
}
object Point {
//因为在 apply 方法中调用了 new point ,所以就会在 apply 方法中返回一个 Point 。
def apply(x:Int=0,y:Int =0)= new Point(x,y)
测试:
object Exercise02 {
def main(args: Array[string]): unit = {
//两个值分别代表横坐标和纵坐标。
//触发 apply 方法。
Val point=point(3,4).
Println(point)
}
}
运行结果:返回了一个 point 实例。
3、编写一个 Scala 应用程序,使用 App 特质,以反序打印命令行参数,用空格隔开。举例来说,scala Reverse Hello World 应该打印 World Hello。
解析:App 特质中包含了 main 方法。所以继承了 App 特质后就可以直接执行语句,当作程序的入口。
答案:
object Exercise03 { extends App{
//app 中隐藏了一个主函数 def main(args:Array[String]):unit= 。因为 app 中已经定义好了一个主函数,并且参数就是 args,所以这里可以直接使用参数 args。
Val args2=args.reverse
Println(args2)
}
//首先可以随便打印一个内容使 idea 能够被识别。
object Exercise03 { extends App{
Println(“hello~~”)
}
找到菜单栏的 Edit Configurations ,点击 program arguments 程序参数,在其中添加内容:hello world abc。运行程序,打印出一个地址:
[L java.lang.String;@3498ed。重新修改代码为 Println(args2.mkstring(“”)),打印结果:abc world hello。
4、编写一个扑克牌4种花色的枚举,让其 toString 方法分别返回,,,,并实现一个函数,检查某张牌的花色是否为红色。
解析:Type 的用法讲解。
//type 用于给类型起别名。Type myint =int 表示 myint 可以当作 int 使用,相当于给 int 取了一个别名,或者自定义了一个类型。
Type myint =int
//把888赋给了 num1,类型是 myint,myint 就是 int 。
Val num1:myint=888
Println(“num1=”+num1)
}
}
运行代码输出:num1=888
答案:
// Enumeration 是枚举。
object Suits extends Enumeration{
//给 Value 类型取别名。
type Suits = Value
///使用 value 中的apply方法创建了一个实例,此 value 是从Enumeration 中继承来的。
val Spade = Value("")
val Club = Value("")
val Heart = Value("���")
val Diamond = value(“”)
//这是 Suits 中的四个实例
//重写 toString 方法
override def toStringo:String={
Suits.values.mkString(",")
}
}
//打印结果。
Println(suits)
}
Suits 是一个 object ,这样相当于输出了 suits 对应的隐藏在底层的module$。由于重写了 to string 方法,所以就会调用 Suits.values 方法,而 Suits.values 则会将所有的实例都取出来。
运行结果:输出了四个花色,如果没有 Suits.values.mkString(",") 则表示不输出任何内容。
//验证花色是否为红色。def isRed(card: Suits)= card == Heart || card == Diamond,在 isRed 中接受一个 Suits然后判断是否为���或者。
def isRed(suits:suits):Boolean={
if suit== Heart || suit==Diamond)
true
}else{
False
}
}
}
这样的写法不够简便,所以直接写 def isRed(card: Suits)= card == Heart || card == Diamond 即可。
测试:object 中的方法可以直接调用。Suits.isRed() 要求传入花色的一种,而花色已经在 suits 中了,所以调用时直接点击 suits 就会显示出现四个实例 club,Diamond,Heart,Spade。
//传入一个红心,应该返回 true。
Suits.isRed(suits.Heart)
//传入一个黑桃,应该返回 false。
Suits.isRed(suits.spade)
//注意用 println 函数打印,否则不能输出结果。
Println(Suits.isRed(suits.Heart))
Println(Suits.isRed(suits.spade))
运行结果:
true
false
二、继承
1、扩展如下的 BankAccount 类,新类 CheckingAccount 对每次存款和取款都收取1美元的手续费。
解析:题目已经给出了 BankAccount 类,要求根据这个类写一个新类 CheckingAccount。 相当于 BankAccount 是普通银行账号类,而 checkingAccount 是基于 BankAccount 进行扩展的账号类。
这是题目给出的普通账号类:
//初始化余额
class BankAccount (initialBalance:Double){
private var balance = initialBalance
//存款,直接在余额上加上存款。
def deposit (amount :Double)= { balane += amount; balance}
//取款,直接从余额减去取款额度并且返回余额。
//此代码还可以加入对余额的判断。
def withdraw(amount : Double)= {balance -= amount; balance}
}
答案:
新类 CheckingAccount 对每次存款和取款都收取1美元的手续费。 BankAccount 类的代码都可以继承过来,但是存款和取款的方法需要重写。
//继承 BankAccount,将 deposit,withdraw进行 overwrite。比如存款存了9元,那么在余额中体现为8元,取款取了10元,但实际上余额会减掉11元。
class BankAccount (initialBalance: Double){
private var balance = initialBalance
def deposit(amount:Double)={balance+=amount;balance}
def withdraw(amount:Double)={balance-=amount;balance}
}
//利用主构造器调用了 BankAccount 的主构造器。
class CheckingAccount(initialBalance:Double) extends BankAccount (initialBalance){
//重写方法时不需要完全重写,用 super 继承即可。
override def deposit(amount:Double)=super.deposit (amount-1)
override def withdraw(amount:Double)=super.withdraw (amount+1)
}
//为了测试结果是否正确,在主类下加入一个查询余额的方法。
Def query():unit={
Ptintln(“当前余额为”+this.balance)
测试:
object BankAccountDemo {
def main(args:Array[String]):unit={
//存入100元。运行结果:当前余额为100.0
val checkingAccount = new checkingAccount(100)
checkingAccount.query()
//取出10元并查询余额。由于手续费是1美元,所以取出10元后余额应该是89元。运行结果:当前余额为89.0
checkingAccount.withdraw(10)
checkingAccount.query()
//存入10元并查询余额。由于手续费是1美元,所以存入10元后余额应该是98元。运行结果:当前余额为98.0
checkingAccount.deposit(10)
checkingAccount.query()
}
}
2、扩展前一个练习的 BankAccount 类,新类 SavingsAccount 每个月都有利息产生(earnMonthlyInterest方法被调用),并且有每月三次免手续费的存款或取款。在 earnMonthlyInterest 方法中重置交易计数。
答案:
//普通账号
class BankAccount(initialBalance: Double){
private var balance = initialBalance
def deposit(amount:Double)={balance+=amount;balance}
def withdraw(amount:Double)={balance=amount;balance}
}
Class savingsAccount (initialBalance:Double) extends
BankAccount(initialBalance){
//定义了一个免手续费的次数
private var num:Int=_
//每月初,系统调用该方法计算利息,同时将 num=3。
def earmonthlyinterest()={
num=3
//一般利息计算应该是余额*利率,这里假定利息是一美元。
super.deposit(1)
}
//取款时如果 num<0就收手续费,否则不收手续费。
override def deposit(amount:Double):Double ={
num-=1
if(num<0)
super.deposit(amount-1)
else
super.deposit(amount)
}
//取款逻辑和存款一样,如果 num<0存款就会收手续费,否则不收。
override def withdraw(amount:Double):Double ={
num-=1
if(num<0) super. withdraw(amount-1) else super. Withdraw (amount)
}
}
解决该问题需要有思维的转换,充分利用父类对子类进行扩展。
测试:
//查看当前账户余额应该是100元。
val savingAccount = new savingAccount(100)
savingAccount.query()
//月初计算利息,账户余额应为101元。
savingAccount.earnMonthlyinterest()
//取款三次,每次取出10元。
savingAccount.withdraw(10)
savingAccount.withdraw(10)
savingAccount.withdraw(10)
//查询账户余额,此时账户余额应是71元。
savingAccount.query()
运行结果:
当前余额为100.0
当前余额为71.0
//再次取出10元,此时还应该扣除手续费,所以账户余额应该是71-10-1=60。
savingAccount.withdraw(10)
savingAccount.query()
}
}
运行结果:当前余额为60.0
3、设计一个 point 类,其 X 和 Y 坐标可以通过构造器提供。提供一个子类 LabeledPoint,其构造器接受一个标签值和 x,y 坐标,比如:newLabeledPoint(“Black Thursday” ,1929,230.07)
解析:本题主要考察构造器的使用。
答案:
class Point(x:Double,y:Double)
//使用了扩展,可以接收两个 double 和一个字符串。
//表示在构建的同时,把 x 和 y 交给父类进行构建。
class LabeledPoint(tag:string x:Double,y:Double) extends Point(x,y)
4、定义一个抽象类 shape,一个抽象方法 centerPoint(求出中心点),以及该抽象类的子类 Rectangle 和 Circle。为子类提供合适的构造器,并重写 enterPoint 方法。
解析:本题主要考察抽象类的使用。由于 Rectangle 和 Circle 没有给出具体的要求,所以从语法层面给出方法即可。
答案:
//抽象类
abstract class Shape{
//抽象方法
def centerPoint()
}
//定义了一个 Rectangle 类,Rectangle 类中有它的坐标。
//这里只给出了方法,内容是空着的。
class Rectangle(startX:Int,startY:Int,endX:Int,endY:Int) extends Shape{
def centerPoint(){}
}
// Circle 以及半径,内容也是空的。
class Circle(x:Int,y:Int,radius:Double) extends shape{
def centerPoint(){}
}
5、提供一个 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)
}
}

