scala的继承、组合与特质trait

Scala特性详解
scala可以在子类的构造器中重写父类的属性,例如:
abstractclassElement{
defcontents:Array[String]
valheight:Int =contents.length
valwidth:Int =if(height==0)0elsecontents(0).length
}
子类:
classUniformElement(u:Char,overridevalwidth:Int,overridevalheight:Int)extendsElement{
privatevalline=u.toString()*width
defcontents =Array.fill(height)(line)
}
可以看到父类的height、width在子类的构造器中,以参数的形式被重写了。
聚义例子:
importElement.elem

abstractclassElement{
defcontents:Array[String]
defheight:Int =contents.length
defwidth:Int =if(height==0)0elsecontents(0).length
defabove(that:Element):Element=
elem(this.contents++that.contents)
defbeside(that:Element):Element={
elem(for((line1,line2) <-this.contentszipthat.contents)yieldline1+line2)
}
overridedeftoString=contentsmkString"/n"
}

objectElement{
privateclassArrayElement(valcontents:Array[String])extendsElement
privateclassLineElement(s:String)extendsElement{
valcontents=Array(s)
overridedefwidth=s.length
overridedefheight=1
}
privateclassUniformElement(u:Char,overridevalwidth:Int,overridevalheight:Int)extendsElement{
privatevalline=u.toString()*width
defcontents=Array.fill(height)(line)
}
defelem(contents:Array[String]):Element=
newArrayElement(contents)
defelem(s:String):Element=
newLineElement(s)
defelem(u:Char,width:Int,height:Int):Element=
newUniformElement(u,width,height)
}
scala中的AnyRef是所有自定义类型的父类,类似于java中的Object,在AnyRef中包含以下方法。
eq:用来比较两个对象是否引用相等,若相等返回True。
ne:eq的反操作。
==:作用和equals相同,用来比较两个对象是否自然相等。
override
这个关键字在子类重写父类中有具体实现的方法的时候必须写override这个关键字,如果子类重写的方法在父类中是抽象方法,那么这个关键字是可选的。

常用特质trait
Ordered[A]:
任何需要使用">","<",">=","<="做比较的类都可以继承这个特质,继承这个特质之后,只需实现compare方法就能让自定义类直接使用上面的4个操作符进行比较。
例子:
classRational(n:Int,d:Int)extendsOrdered[Ratinal]{
//...
defcompare(that:Rational) =
(this.number*that.denom)-(this.denom*that.number)
}

特质trait的堆叠改变
使用trait中super的动态绑定来实现特质的堆叠
trait的堆叠和普通情况有一个大区别就是trait要完成堆叠一定要继承一个class
第1步:定义一个抽象类
abstractclassIntQueue {
defget():Int
defput(x:Int)
}
第2步:定义一个实现类
classBasicIntQueueextendsIntQueue {
privatevalbuf=newArrayBuffer[Int]
defget=buf.remove(0)
defput(x:Int) {buf+=x}
}
第3步:定义几个继承了抽象类IntQueue的特质:
traitIncrementingextendsIntQueue {
abstractoverridedefput(x:Int){super.put(x+1)}
}
traitFilteringextendsIntQueue {
abstractoverridedefput(x:Int){if(x>=0)super.put(x)}
}
第4步:使用特质的堆叠:
可以是:
valqueue= (newBasicIntQueuewithIncrementingwithFiltering)
queue.put(-1)
queue.put(0)
queue.put(1)
println(queue.get())
println(queue.get())
也可以是:
classMyQueueextendsBasicIntQueuewithDoubling

valqueue=newMyQueue
queue.put(-1)
queue.put(0)
queue.put(1)
println(queue.get())
println(queue.get())
以上第4步的输出为:1,2
特质混入的次序是重要的,简单的说,越靠近右边的会越优先起作用,从右边开始,如果调用的那个方法带super的话,他调用其左侧的方法,以此类推。
特质堆叠顺序说明:

对于以上特质继承了类在说明一点注意,继承了以上Furry、FourLegged后的类默认继承了超类Animal所以由于java单继承的原因,就不能再继承别的类了。
混入对象的构造器的执行顺序:
  • 首先调用超类的构造器
  • 特质的构造器在超类构造器之后、类构造器之前执行
  • 特质从左到右被构造
  • 每个特质中,父特质先被构造
  • 如果多个特质共有一个父特质、而那个父特质已经被构造,则不会被再次构造
  • 所有的特质被构造完毕,子类被构造
以上面的Cat类为例:
class Cat extends Animal with Furry with FourLegged
lin(Cat)
=Cat>>lin(FourLegged)>>lin(Furry)>>lin(Animal)
=Cat>>(FourLegged>>lin(Haslegs))>>(Furry>>Animal)>>Animal
=Cat>>(FourLegged>>(Haslegs>>Animal))>>(Furry>>Animal)>>Animal
=Cat>>FourLegged>>Haslegs>>Furry>>Animal

这里的,>>意思是“串接并去掉重复项,右侧胜出”

trait
scala中的trait定义特质类,这个关键字的作用类似于java中的interface,但是在scala中,特质类中除了抽象方法以外,还能有非抽象的方法,这就和java中的interface不同了。类可以通过extends或with关键字来实现特质,如果使用extends就只能继承一个,使用with就能继承多个。
trait中定义了字段,在类混入该trait的时候,这些字段不是被继承而是单纯的添加到子类当中
trait也有构造器,trait的构造器就是在trait中没有写在方法中的代码块。属于一个无参构造器
trait还引申出一个自身类型的概念:
当特质以如下代码开始定义时
this:类型 =>
它便只能被混入制定类型的子类
例1:
trait LoggedException{
this:Exception=>def log(){....)}
注意该特质并不扩展Exception类,而是有一个自身类型Exception,这意味着,它只能被混入Exception子类。
例2:
trait LoggedException{
this : {def getMessage():String}=>def log(){....)}
这个特质可以被混入任何拥有getMessage方法的类。

trait和class的区别:
trait可以当做一个特殊的类来使用,trait与class比较有以下两条区别:
1.class中的super关键字是静态绑定的,但是tarit的super关键字是动态绑定的也trait的super只有在使用的时候才知道super调用哪个,正因为这样,所以才有上面说到的堆叠改变。
2.特质不能有任何类参数,即传递给主构造器的参数。

trait和java中interface的区别:
scala中的trait定义特质类,这个关键字的作用类似于java中的interface,但是在scala中,特质类中除了抽象方法以外,还能有非抽象的方法,这就和java中的interface不同了。类可以通过extends或with关键字来实现特质,如果使用extends就只能继承一个,使用with就能继承多个。

scala中的泛型一般来说是非协变的(或者说是严谨的),也就是说Queue[T]是泛型,S是T的子类型,但是Queue[S]不是Queue[T]的子类型,这种形式称之为严谨的。
但是如果将Queue[T]的定义改为Queue[+T]那么泛型Queue就是协变的。
如果Queue的定义为Queue[-T]那么泛型Queue就是逆变的

抽象的val只能用val重写不能用def重写,因为val限制了重写的次数。抽象的def可以用val或者def重写。以上两句就是要说明val比def严谨。






评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值