特徵不僅可規範抽象、沒有任何實作內容的方法,特徵中還可以撰寫有實作內容的方法。透過適當的設計,你可以將物件之間可能共用的實作抽離至特徵中,在必要時讓類別繼承或具有特徵,使得類別定義時得以精簡。
舉個例子來說,你會定義以下的球類別,並定義了一些比較大小的方法:
class Ball(val radius: Int) {
def volume = 4 * Math.Pi * Math.pow(radius, 3) / 3
def < (that: Ball) = this.radius - that.radius < 0
def <=(that: Ball) = (this < that) || (this == that)
def > (that: Ball) = !(this <= that)
def >=(that: Ball) = !(this < that)
override def equals(a: Any) = a match {
case that: Ball => this.radius == that.radius;
case _ => false
}
override def hashCode = 41 * radius
}
事實上,比較大小順序這件事,許多物件都會用的到,仔細觀察以上的程式碼,你會發現可抽離的共用比較方法,你可以將之重新設計為特徵:
trait Ordered {
def compare(that: Any): Int
def < (that: Any) = compare(that) < 0
def <=(that: Any) = (this < that) || (this == that)
def > (that: Any) = !(this <= that)
def >=(that: Any) = !(this < that)
}
特徵中除了compare()沒有實作之外,其它的方法都實作了。現在有了Order特徵,你可以在設計球類別時更為精簡,如果你需要彼此比較的功能,則只要繼承Order特徵並實作compare()方法即可以:
class Ball(val radius: Int) extends Ordered {
def volume = 4 * Math.Pi * Math.pow(radius, 3) / 3
def compare(a: Any) = a match {
case that: Ball => this.radius - that.radius;
case _ => throw new IllegalArgumentException("不是球比什麼?")
}
override def equals(a: Any) = a match {
case that: Ball => this.radius == that.radius;
case _ => false
}
override def hashCode = 41 * radius
}
val b1 = new Ball(10)
val b2 = new Ball(20)
println(b1 > b2) // false
println(b1 >= b2) // false
println(b1 < b2) // true
println(b1 <= b2) // true
println(b1 == b2) // false
事實上,Scala確實有提供scala.Ordered[A]特徵來作比大小這種事:
package scala
trait Ordered[A] {
def compare(that: A): Int
def < (that: A): Boolean = (this compare that) < 0
def > (that: A): Boolean = (this compare that) > 0
def <= (that: A): Boolean = (this compare that) <= 0
def >= (that: A): Boolean = (this compare that) >= 0
def compareTo(that: A): Int = compare(that)
}
trait Ordered[A] {
def compare(that: A): Int
def < (that: A): Boolean = (this compare that) < 0
def > (that: A): Boolean = (this compare that) > 0
def <= (that: A): Boolean = (this compare that) <= 0
def >= (that: A): Boolean = (this compare that) >= 0
def compareTo(that: A): Int = compare(that)
}
Ordered[A]可以在使用時宣告A的型態,例如,上例可以使用Ordered[A]來改寫如下,而結果仍相同:
class Ball(val radius: Int) extends Ordered[Ball] {
def volume = 4 * Math.Pi * Math.pow(radius, 3) / 3
def compare(that: Ball) = this.radius - that.radius
override def equals(a: Any) = a match {
case that: Ball => this.radius == that.radius;
case _ => false
}
override def hashCode = 41 * radius
}
val b1 = new Ball(10)
val b2 = new Ball(20)
println(b1 > b2) // false
println(b1 >= b2) // false
println(b1 < b2) // true
println(b1 <= b2) // true
println(b1 == b2) // false
特徵可以定義抽象方法,這可以用來規範物件間必須共同實作的介面,特徵可以定義有具體實作的方法,這可以用來將一些可能會共用的實作或操作獨立出來,在必要時讓類別繼承或具有特徵,讓物件本身在定義時得以精簡。