Scala实验任务二
任务要求:
对于一个图形绘制程序,用下面的层次对各种实体进行抽象。定义一个 Drawable 的特 质,其包括一个 draw 方法,默认实现为输出对象的字符串表示。定义一个 Point 类表示点, 其混入了 Drawable 特质,并包含一个 shift 方法,用于移动点。所有图形实体的抽象类为Shape,其构造函数包括一个 Point 类型,表示图形的具体位置(具体意义对不同的具体图 形不一样)。Shape 类有一个具体方法 moveTo 和一个抽象方法 zoom,其中 moveTo 将图形从 当前位置移动到新的位置, 各种具体图形的 moveTo 可能会有不一样的地方。zoom 方法实 现对图形的放缩,接受一个浮点型的放缩倍数参数,不同具体图形放缩实现不一样。继承 Shape 类的具体图形类型包括直线类 Line 和圆类 Circle。Line 类的第一个参数表示其位置, 第二个参数表示另一个端点,Line 放缩的时候,其中点位置不变,长度按倍数放缩(注意, 缩放时,其两个端点信息也改变了),另外,Line 的 move 行为影响了另一个端点,需要对 move 方法进行重载。Circle 类第一个参数表示其圆心,也是其位置,另一个参数表示其半 径,Circle 缩放的时候,位置参数不变,半径按倍数缩放。另外直线类 Line 和圆类 Circle 都混入了 Drawable 特质,要求对 draw 进行重载实现,其中类 Line 的 draw 输出的信息样式 为“Line:第一个端点的坐标--第二个端点的坐标)”,类 Circle 的 draw 输出的信息样式为 “Circle center:圆心坐标,R=半径”。如下的代码已经给出了 Drawable 和 Point 的定义, 同时也给出了程序入口 main 函数的实现,请完成 Shape 类、Line 类和 Circle 类的定义。
case class Point(var x:Double,var y:Double) extends Drawable{ def shift(deltaX:Double,deltaY:Double){x+=deltaX;y+=deltaY}
}
trait Drawable{
def draw(){println(this.toString)}
}
// 请完成 Shape 类、Line 类和 Circle 类的定义。
object MyDraw{
def main(args: Array[String]) { val p=new Point(10,30)
p.draw;
val line1 = new Line(Point(0,0),Point(20,20)) line1.draw
line1.moveTo(Point(5,5)) //移动到一个新的点
line1.draw line1.zoom(2) //放大两倍 line1.draw
val cir= new Circle(Point(10,10),5) cir.draw
cir.moveTo(Point(30,20)) cir.draw
cir.zoom(0.5) cir.draw
}
Shape:
因为shape中有抽象类所以需要引用abstract
Line类:
当一个类extends另外一个类的时候,override的规则基本如下:
- 子类中的方法要覆盖父类中的方法,必须写override(参见foo)
- 子类中的属性val要覆盖父类中的属性,必须写override(参见nameVal)
- 父类中的变量不可以覆盖(参见nameVar)
Scala中的override
override是覆盖的意思,在很多语言中都有,在scala中,override是非常常见的,在类继承方面,它和java不一样,不是可写可不写的了,而是必须写的。如果不写而覆盖了对应的属性或者方法的话,编译器就会报错了。今天把scala中的override的各种地方都整理了一遍,以方便以后翻阅。
基础用法
/* 基本的override特性 */ class A { val nameVal = "A" var nameVar = "A" def foo: String = { "A.foo" } } class B extends A { override val nameVal = "B" //override var nameVar = "B" "variable nameVar cannot override a mutable variable" override def foo: String = { "B.foo" } } val b1 = new B b1.foo b1.nameVal b1.nameVar val b2 : A = new B b2.foo b2.nameVal b2.nameVar = "B" b2.nameVar 输出: defined class A defined class B b1: B = 9825fab res0: String = B.foo res1: String = B res2: String = A b2: A = B@c46c4a1 res3: String = B.foo res4: String = B b2.nameVar: String = B res5: String = B
当一个类extends另外一个类的时候,override的规则基本如下:
- 子类中的方法要覆盖父类中的方法,必须写override(参见foo)
- 子类中的属性val要覆盖父类中的属性,必须写override(参见nameVal)
- 父类中的变量不可以覆盖(参见nameVar)
在抽象类中可以不用写override
/* trait的extent不需要override */ trait T { def foo : String def bar : String } class TB extends T { def foo: String = { "TB.foo" } def bar: String = "TB.bar" } val tb = new TB tb.foo tb.bar trait TT extends T { def bar :String = "TT.bar" } class TTB extends TT { def foo: String = "TTB.foo" } val ttb = new TTB ttb.foo ttb.bar 输出: defined trait T defined class TB tb: TB = 2fb497ea res6: String = TB.foo res7: String = TB.bar defined trait TT defined class TTB ttb: TTB = 346c06af res8: String = TTB.foo res9: String = TT.bar
T是特性类,它定义了两个抽象方法,foo和bar。TB的类继承和实现了T特性类,这个时候,TB类中的foo和bar前面的override是可写可不写的。这里初步看下TB类中的foo和bar前面的override写和不写感觉都一样,但是一旦有钻石结构的类继承,这个override的作用就体现出来了。这个我们后续说。
TT和TTB的例子也是说明了下trait继承trait是不需要使用override的。
abstrct class 也不需要使用override
/* abstrct class 不需要override */ abstract class PA(name: String) { def hello: String } class PB(name: String) extends PA(name) { def hello : String = s"hello ${name}" } val pb = new PB("yejianfeng") pb.hello 输出: defined class PA defined class PB pb: PB = 62840167 res10: String = hello yejianfeng abstract class和trait的特性主要是在是否有构造参数,在override方面都是一样的。
钻石结构
所谓的钻石结构就是一个菱形的结构,一个基类,两个子类,最后一个类又继承这两个子类。那么如果这两个子类都包含一个基类的方法,那么最后的这个类也有这个方法,选择继承那个子类呢?
/* 钻石结构 */ trait Animal { def talk: String } trait Cat extends Animal { def talk: String = "I am Cat" } trait Monkey extends Animal { def talk: String = "I am monkey" } trait Dog extends Animal { override def talk: String = "I am Dog" } val kittyDog = new Cat with Dog kittyDog.talk class MonkeyCat extends Monkey with Cat { override def talk: String = "I am monkeyCat" } val monkeyCat = new MonkeyCat monkeyCat.talk 输出: defined trait Animal defined trait Cat defined trait Monkey defined trait Dog kittyDog: Cat with Dog = $anon$5378ef6d res11: String = I am Dog defined class MonkeyCat monkeyCat: MonkeyCat = 1e444ce6 res12: String = I am monkeyCat
在这个例子中,Animal是基类,Cat和Dog是子类,kittyDog是继承了Cat和Dog,那么kittyDog里面的talk使用的是Cat和Dog中有标示override的那个方法。这个时候override的作用就体现出来了。
参数复写使用override
我们可以直接在构造函数里面使用override重写父类中的一个属性。我理解这个更多是语法糖的一个功能。
/* 参数复写 */ class Person(val age : Int){ val name = "no name" } class XiaoMing(age: Int, override val name: String) extends Person(age){ } val xiaoming = new XiaoMing(12, "xiaoming") xiaoming.name 输出: defined class Person defined class XiaoMing xiaoming: XiaoMing = 2eef0f3c res13: String = xiaoming转自:https://www.cnblogs.com/yjf512/p/8026611.html
最终效果: