您的位置:首页 > 大数据 > 人工智能

scala--trait 特质

2018-03-10 17:29 351 查看

scala--trait 特质

        Scala Trait(特征) 相当于 Java 的接口,实际上它比接口还功能强大。        与接口不同的是,它还可以定义属性和方法的实现。即trait支持部分实现,也就是说可以在scala的trait中可以实现部分方法。其中可有抽象方法和非抽象方法。从这一点上看,trait与java的抽象类更类似。    在scala官方文档中有这样的说法:Traits are used to share interfaces and fields between classes. They are similar to Java 8’s interfaces. Classes and objects can extend traits but traits cannot be instantiated and therefore have no parameters.

Defining a trait

A minimal trait is simply the keyword 
trait
 and an identifier:trait HairColorTraits become especially useful as generic types and with abstract methods.trait Iterator[A] {
def hasNext: Boolean
def next(): A
}Extending the 
trait Iterator[A]
 requires a type 
A
 and implementations of the methods 
hasNext
 and 
next
.

Using traits
Use the 
extends
 keyword to extend a trait. Then implement any abstract members of the trait using the 
override
 keyword:
1. 继承trait必需要实现trait中所有的 抽象方法,非抽象方法可根据需要选择实现2. 实现类中的 用以实现抽象方法的方法前可加override 关键字,3.实现类中的 用以实现非抽象方法的方法前必须加override 关键字,4.所有的Java接口都可以作为Scala特质来使用。与Java一样,Scala类只能有一个超类,可以有任意数量的特质。

根据快学scala的内容,以下对trait做一个简要说明:1. 把特质当作接口使用:        a) trait中未被实现的方法默认是抽象方法,方法前加abstract。
        a) trait实现时用的extends而不是implements,
        c) 实现trait的抽象方法的方法前无需override 关键字, 实现trait的非抽象方法的方法前必须加override 
       d) 所有Java接口都可以作为Scala特质使用。
2. 带有具体实现的特质         当trait的实现类中调用了特质中的某一非抽象方法,在trait的非抽象方法改变时,所有混入了该trait的类都要重新编译,说明见以下示例:
package traitWithImpl{
trait Logger{
def log(msg : String) = println(msg)
}
class LoggerImpl1 extends Logger{
def fun1(message :String): Unit ={
log(message);
}
}
class LoggerImpl2 extends Logger{
def fun1(message :String): Unit ={
log(message);
}
}
}

176b1
//当Logger 中的log方法改变时,其实现类中调用log()方法的
// LoggerImpl1 、LoggerImpl2均需要重新编译3. 带有特质的对象   package objwithtrait{
trait Logger{
def log(msg :String){}
}
class SavingsAccount extends Logger{
def fun(msg:String): Unit ={
log(msg)
}
}
//貌似上面的代码毫无意义,但是我们可以在构造具体对象的时候
// 混入一个更好的实现,如下所示:
trait ConsoleLogger extends Logger{
override def log(msg :String){println(msg)}
}
object Main{
def main(args: Array[String]) {
//我们在构造SavingsAccount对象sa的时候加入ConsoleLogger特质,如下所示:
val sa = new SavingsAccount with ConsoleLogger
sa.fun("hahahahah") //打印 hahahahah
}
}
}
4. 叠加在一起的特质       可以为类和对象添加多个
相互调用的特质
时, 从最后一个开始调用. 这对于需要分阶段加工处理某个值的场景很有用.package foldtrait{
import java.util.Date

import scala.collection.mutable.ArrayBuffer

//叠加在一起的trait
trait Logger{
def log(msg : String) = println(msg)
}
trait TimestampLogger extends Logger{
override def log(msg:String): Unit ={
println("timestamp-------")
super.log(new Date()+""+msg)
}
}
trait ShortLogger extends Logger{
val maxLen = 30
override def log(msg:String): Unit ={
println("ShortLogger-------")
super.log(
if (msg.length <= maxLen) msg else msg.substring(0,maxLen)
)
}
}
//特别注意的是,super.log并不像类那样拥有相同的含义,当然如果含义相同,那么这些特质毫无用处,
// 因为这些特质扩展的Logged特质中的log()什么也不做。实际上,super.log调用的是特质层级中的下一个特质,
// 具体是哪一个,要根据特质添加的顺序来决定。
class SavingAccount extends Logger{
def fun(msg:String){
log(msg)
}
}
abstract class CharBuffer{
def get:Char
def put(c:Char)
}
class Overlay extends CharBuffer{
val buf = new ArrayBuffer[Char]
def get():Char = if(buf.length != 0)buf(0) else '@'
def put(c:Char){buf.append(c)}
}
trait ToUpper extends CharBuffer{
abstract override def put(c:Char) =super.put(c.toUpper)
}
trait ToLower extends CharBuffer{
abstract override def put(c:Char) = super.put(c.toLower)
}
object Main{
def main(args: Array[String]) {
val ol1 = new Overlay with ToLower with ToUpper
val ol2 = new Overlay with ToUpper with ToLower
ol1.put('A')
println(ol1.get())
ol2.put('A')
println(ol2.get())
/*结果
a
A
*/

val sa1 = new SavingAccount with TimestampLogger with ShortLogger
val sa2 = new SavingAccount with ShortLogger with TimestampLogger
sa1.fun("#################")
sa2.fun("#################")
/*其结果如下
ShortLogger-------
timestamp-------
Sun Mar 11 10:28:07 CST 2018#################
//这说明ShortLogger中的log()首先被执行,然后它的super.log调用的是TimestampLogger。
timestamp-------
ShortLogger-------
Sun Mar 11 10:28:07 CST 2018##
//这说明TimestampLogger中的log()首先被执行,然后它的super.log调用的是ShortLogger,其结果在之后被截断。
*/
}
}
}上面代码的一些说明:

上面的特质继承了超类charBuffer, 意味着这两个特质只能混入继承了charBuffer的类中
上面每一个
put
方法都将修改过的消息传递给 
super.put
, 对于特质来说, super.put 调用的是
特质层级
的下一个特质(下面说), 具体是哪一个根据特质添加的顺序来决定. 一般来说, 特质从最后一个开始被处理.
在特质中,由于继承的是抽象类,super调用时非法的。这里必须使用abstract override 这两个关键字,在这里表示特质要求它们混入的对象(或者实现它们的类)具备 put 的具体实现, 这种定义仅在特质定义中使用。
混入的顺序很重要,越靠近右侧的特质越先起作用。当你调用带混入的类的方法时,最右侧特质的方法首先被调用。如果那个方法调用了super,它调用其左侧特质的方法,以此类推。
如果要控制具体哪个特质的方法被调用, 则可以在方括号中给出名称: super[超类].put(...), 这里给出的必须是直接超类型, 无法使用继承层级中更远的特质或者类; 不过在本例中不行, 由于两个特质的超类是抽象类, 没有具体方法, 编译器报错   
5. 在特质中重写抽象方法  6. 当做富接口使用的特质  7. 特质中的具体字段   8. 特质中的抽象字段  9. 特质构造顺序 10. 初始化特质中的字段  11. 扩展类的特质

一般情况下Scala的类只能够继承单一父类,但是如果是 Trait(特征) 的话就可以继承多个,从结果来看就是实现了多重继承。Trait(特征) 定义的方式与类类似,但它使用的关键字是 trait。以下示例中,trait 由两个方法组成:isEqual 和 isNotEqual。isEqual 方法没有定义方法的实现,isNotEqual定义了方法的实现。子类继承特征可以实现未被实现的方法。package traitTest {

trait Equal {
def isEqual(x: Any): Boolean
def isEqual1(x: Any): Boolean

def isNotEqual(x: Any): Boolean = !isEqual(x)
}

class Person(name: String, age: Int) extends Equal {
var n = name
var a = age
override def isEqual(obj: Any): Boolean = {
obj.isInstanceOf[Person] && obj.asInstanceOf[Person].n == n
}
def isEqual1(obj: Any): Boolean = {
obj.isInstanceOf[Person] && obj.asInstanceOf[Person].n == n
}
override def isNotEqual(obj:Any):Boolean={
println("=====")
super.isNotEqual(obj) || obj.asInstanceOf[Person].a != a}

}
object TraitTest {
def main(args: Array[String]) {
val p1 = new Person("hello",1)
val p2 = new Person("hello1",11)
val p3 = new Person("hello",1)
println(p1.isEqual(p2))
println(p1.isEqual(p3))
println(p1.isNotEqual(p2))

}
}
}结果:false
true
=====
true
下面再看个例子,看看trait是如何使用的package traitTest2{
abstract class Animal{
def walk(speed:Int)
def breathe() = {
println("animal breathes...")
}
}
trait Flyable{
def hasFather = true
def fly
}
trait Swimming{
def swim
}
class Fish extends Animal with Flyable with Swimming{
def swim()= {
println("fish swim")
}
def walk(speed :Int): Unit ={
println("fish walk with speed "+ speed)
}
def fly: Unit ={
println("fish fly fast")
}
override def hasFather:Boolean = {
false
}

}
}Fish类继承自Animal,extends Animal后面有两个with,with Flyable和with Swimming,表示也具备两种特征。在类的实现中需要实现抽象类Animal的walk方法,也需要实现两个特征中定义的未实现方法。5.类在继承/实现 抽象类/trait 时,当需继承/实现多个抽象类/trait时,第一个继承的关键字用extends 后面的用withFish 的调用同class的实例化调用,再通过实例化对象调用方法即可,此处不再赘述。trait的使用方法就是这样子了,它很强大,抽象类能做的事情,trait都可以做。它的长处在于可以多继承。trait和抽象类的区别在于抽象类是对一个继承链的,类和类之前确实有父子类的继承关系,而trait则如其名字,表示一种特征,可以多继承。

当做富接口使用的特质

        在Scala中,在特质中混用具体方法和抽象方法十分普遍;而Java中需要声明一个接口和一个额外的扩展该接口的类
package richInterface{
trait Logger{
def log(msg:String)
def info(msg:String){log("INFO : "+msg)}
def warn(msg:String){log("WARN : "+msg)}
}
class Class extends  Logger{
def log(msg:String): Unit ={
println(msg)
}
}
object Main{
def main(args: Array[String]) {
val c = new Class
c.log("rich interface...")
c.info("hello world...")
c.warn(".....")
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  trait oop