在 Scala中Trait 为重用代码的一个基本单位。
将traits作为Java中的接口使用,一个 Traits 封装了方法和变量(但和 Interface 相比,它的方法可以有实现,这一点有点和抽象类定义类似)。
但和类继承不同的是,Scala 中类继承为单一继承,子类只能有一个父类。但一个类可以和多个 Trait 混合,使用 with 关键字即可。这些 Trait 定义的成员变量和方法也就变成了该类的成员变量和方法。
类可以使用extends关键字继承trait,(这里不是 implement,而是extends) ,在Scala中没有 implement 的概念,无论继承类还是trait,统一都是 extends;类继承后,必须实现其中的抽象方法,实现时,不需要使用 override 关键字;
由此可以看出 Trait 集合了 Interface 和抽象类的优点,同时又没有破坏单一继承的原则。
package com.fgm.traits
trait SpeakTrait {
def speak:Unit
}
trait EatingTrait{
val eating="food is meat"
}
abstract class Animal{
val age=10
def running:Unit
}
//可以单继承类Animal,可以和多个trait混合
class Dog(val name:String ) extends Animal with SpeakTrait with EatingTrait{
override def speak: Unit = {
println("wangwangwang")
}
def running: Unit = {
println("He is running ")
}
}
object Dog{
def main(args: Array[String]): Unit = {
val dog = new Dog("一条狗")
println(dog.name)
dog.speak
dog.running
println(dog.age)
println(dog.eating)
}
}
Scala中的trait不仅可以定义抽象方法,还可以定义具体的方法,此时 trait 更像是包含了通用方法的工具,可以认为trait还包含了类的功能。
trait 可以定义具体的 field,此时继承 trait 的子类就自动获得了 trait 中定义的 field;
从trait中获取field的方式与继承class获得field的方式不同:
–继承 class 获取的 field ,实际上还是定义在父类中的;
–而继承 trait获取的 field,就直接被添加到子类中。
Scala中的trait也能定义抽象方法和抽象field, 而trait中的具体方法也能基于抽象field编写
继承trait的类,则必须覆盖抽象方法和抽象field,提供具体的值;
package com.fgm.traits
trait SayHello {
val msg:String
def sayHello(name: String) = println(msg + name)
def say:Unit
}
class TraitTest02(val name: String) extends SayHello{
//必须覆盖抽象 field
val msg = "Hello"
def makeFriends(other: TraitTest02) = {
this.sayHello(other.name)
println("I'm " + this.name + ", I want to make friends with you")
}
//必须覆盖抽象方法
override def say: Unit = {
println("this is the method to override abstract method ")
}
}
object TraitTest02{
def main(args: Array[String]) {
val p1=new TraitTest02("Tom")
val p2=new TraitTest02("Jerry")
p1.makeFriends(p2)
//输出结果:HelloJerry
//I'm Tom, I want to make friends with you
}
}
可在创建类的对象时,使用 with 关键字为该对象指定混入某个trait。
且只有混入了trait的对象才具有trait中的方法,而其他该类的对象则没有;
package com.fgm.traits
trait LogTrait {
// 该方法为实现的具体方法
def log(msg: String) = {}
}
trait MyLogger extends LogTrait{
// 覆盖 log() 方法
override def log(msg: String) = println("log: " + msg)
}
class MixTrait(val name: String) extends LogTrait {
def sayHello = {
println("Hi, I'm " + this.name)
log("I'm the trait method")
}
}
object MixTrait{
def main(args: Array[String]) {
val tom= new MixTrait("Tom")
//结果为:Hi, I'm Tom
tom.sayHello
// 使用 with 关键字,指定混入MyLogger trait
val rose = new MixTrait("Rose") with MyLogger
// 输出结果为:Hi, I'm Rose
// 输出结果为:log: I'm the trait method
rose.sayHello
}
}
Scala中支持让类继承多个trait后,可依次调用多个trait中的同一个方法,只要让多个trait中的同一个方法,在最后都依次执行 super 关键字即可;
类中调用多个trait中都有的这个方法时,首先会从最右边的trait的方法开始执行,然后依次往左执行,形成一个调用链条;
这是设计模式中责任链模式的一种具体实现;
在 trait 中,可以混合使用具体方法和抽象方法:可以让具体方法依赖于抽象方法,而抽象方法则可放到继承 trait的子类中去实现;
这是设计模式中的模板设计模式的体现;
trait ValidTrait {
//抽象方法
def getName: String
//具体方法,具体方法的返回值依赖于抽象方法
def valid: Boolean = {"Tom".equals(this.getName)
}
}
class PersonForValid(val name: String) extends ValidTrait {
def getName: String = this.name
}
object PersonForValid{
def main(args: Array[String]): Unit = {
val person = new PersonForValid("Rose")
println(person.valid)
}
}
在Scala中,trait也是有构造代码的,即在trait中,不包含在任何方法中的代码;
继承了trait的子类,其构造机制如下:
父类的构造函数先执行, class 类必须放在最左边;
多个trait从左向右依次执行;
构造trait时,先构造父 trait,如果多个trait继承同一个父trait,则父trait只会构造一次;
所有trait构造完毕之后,子类的构造函数最后执行。
class Person {
println("Person's constructor!")
}
trait Logger {
println("Logger's constructor!")
}
trait MyLogger extends Logger {
println("MyLogger's constructor!")
}
trait TimeLogger extends Logger {
println("TimeLogger's contructor!")
}
class Student extends Person with MyLogger with TimeLogger {
println("Student's constructor!")
}
object exe {
def main(args: Array[String]): Unit = {
val student = new Student
//执行结果为:
// Person's constructor!
// Logger's constructor!
// MyLogger's constructor!
// TimeLogger's contructor!
// Student's constructor!
}
}
该案例中:
(1)在构建class类Student的对象时,其父类Person的构造函数先执行。
(2)之后MyLogger 和TimeLogger 按照顺序从左到右依次执行,也就是先构造trait MyLogger ,由于MyLogger 继承了Logger,所以,在构造MyLogger 时,先构造它的父trait,也就是Logger的构造方法接着执行;
(3)在MyLogger 的父trait Logger 构造执行之后,MyLogger 的构造函数开始执行;
(4)接下来,按照从左到右的顺序,执行TimeLogger ,TimeLogger 也有父trait ,但它的父trait和MyLogger的父trait相同,按照“多个trait继承同一个父trait,则父trait只会构造一次”的原则,这里不再执行Logger的构造函数,所以直接执行TimeLogger 的构造函数。
(5)所有trait构造完毕之后,子类的构造函数最后执行。此时发现所有trait的构造函数均已执行完毕,所以此时执行子类Student的构造函数。
在Scala中trait 也可以继承 class,此时这个 class 就会成为所有继承该 trait 的子类的超级父类.
Ordered:Ordered特质扩展自java的Comparable接口,Ordered特质用于排序,为了使用它,首先将其混入类中,然后实现一个compare方法。
源码参考:
trait Ordered[A] extends Any with java.lang.Comparable[A] {
/** Result of comparing `this` with operand `that`.
*
* Implement this method to determine how instances of A will be sorted.
*
* Returns `x` where:
*
* - `x < 0` when `this < that`
*
* - `x == 0` when `this == that`
*
* - `x > 0` when `this > that`
*
*/
def compare(that: A): Int
/** Returns true if `this` is less than `that`
*/
def < (that: A): Boolean = (this compare that) < 0
/** Returns true if `this` is greater than `that`.
*/
def > (that: A): Boolean = (this compare that) > 0
/** Returns true if `this` is less than or equal to `that`.
*/
def <= (that: A): Boolean = (this compare that) <= 0
/** Returns true if `this` is greater than or equal to `that`.
*/
def >= (that: A): Boolean = (this compare that) >= 0
/** Result of comparing `this` with operand `that`.
*/
def compareTo(that: A): Int = compare(that)
}
object Ordered {
/** Lens from `Ordering[T]` to `Ordered[T]` */
implicit def orderingToOrdered[T](x: T)(implicit ord: Ordering[T]): Ordered[T] =
new Ordered[T] { def compare(that: T): Int = ord.compare(x, that) }
}
Ordering
Application
APP