Scala入門到放棄——特質trait(七)

十、特質trait

類似於Java中的接口

語法

  • 特質關鍵詞trait

  • 特質可以同時用於抽象方法和具體方法

  • 無父類 實現特質extends trait1 with trait2 with trait3 ...

  • 有父類 實現特質extends 父類 with 特質1 with 特質2 ...

當做接口的特質

  1. Scala可以完全像Java接口一樣工作, 你不需要將抽象方法聲明爲 abstract, 特質中未被實現的方法默認就是抽象方法;
  2. 類可以通過 extends 關鍵字繼承特質, 如果需要的特質不止一個, 通過 with 關鍵字添加額外特質
  3. 重寫特質的抽象方法時, 不需要 override 關鍵字
  4. 所有 Java 接口都可以當做 Scala 特質使用 trait Animals extends Serializable with Cloneable
package traits

// 注意: java的接口可以當做scala的trait使用
// trait Animals extends Serializable with Cloneable

// 等價於: public interface Animals {}
trait Animals {
  // 只有方法的聲明,沒有實現
  // 等價於 void eat();
  def eat(): Unit
}

// 如果當前類Dog沒有父類  extends 特質
class Dog extends Animals {
  // 等價於 實現接口中的 public void eat()
  override def eat(): Unit = println("dog eat shit")
}

object AnimalsDemo {
  def main(args: Array[String]): Unit = {
    val dog = new Dog
    dog.eat()
  }
}

帶有具體實現的特質

trait Animals {
  def eat(food: String): String
  def sleep():Unit = println("animals sleep~~~") // 特質帶有方法實現的方法
}

在這裏插入圖片描述

注意:讓特質混有具體行爲有一個弊端. 當特質改變時, 所有混入該特質的類都必須重新編譯

帶有特質的對象 動態混入(mixin) [ 重點]

在構造單個對象時, 你可以爲它添加特質

  • 特質可以將對象原本沒有的方法與字段加入對象中, 功能擴展
  • 無需修改類聲明定義,擴展類的功能,靈活簡便
  • 如果特質和對象改寫了同一超類的方法, 則排在右邊的先被執行
package traits.test1.mixin

trait Animals {
  def eat(): Unit
}

trait Sheep extends Animals {
  // 實現特質聲明的方法
  override def eat() = {
    println("喫草")
  }

  def run = println("在跑...")

  def sleep
}

class Person extends Animals {
  override def eat(): Unit = {
    println("喫食物")
  }
}

object Person {
  def main(args: Array[String]): Unit = {
    val person = new Person with Sheep {
      override def sleep: Unit = println("睡覺覺!!!")
    } // 動態混入
    person.eat // Person 和 Sheep都有eat方法,調用時從右往左調用  輸出 喫草
    person.run // Person 中沒有run方法,Sheep特質將自己的方法混入Person
    person.sleep  // 睡覺覺!!!
  }
}

在這裏插入圖片描述

this別名

看scala的源碼的話很發現很多源碼開頭都有一句:self => 這句相當於給this起了一個別名爲selfself不是關鍵字,可以用除了this外的任何名字命名(除關鍵字)。就下面的代碼,在Student內部,可以用this指代當前對象,也可以用self指代,兩者是等價的。

class Student {
  self => // this起別名
  val x = 2

  def foo = self.x + this.x
}

object Student {
  def main(args: Array[String]): Unit = {
    val s1 = new Student
    println(s1.foo) //4
  }
}

它的一個場景是用在有內部類的情況下:

class Outer {
  outer =>
  val v1 = "here"
  class Inner {
    inner =>
    val v1 = "-------------"
    println(outer.v1) // 用outer表示外部類,相當於Outer.this
    println(inner.v1)
  }
}

self-type

強制混入

自類型用於聲明一個特質必須混入其他特質,儘管該特質沒有直接擴展其他特質。 這使得所依賴的成員可以在沒有導入的情況下使用。

自類型是一種細化 thisthis 別名之類型的方法。 語法看起來像普通函數語法,但是意義完全不一樣。

要在特質中使用自類型,寫一個標識符,跟上要混入的另一個特質,以及 =>(例如 someIdentifier: SomeOtherTrait =>

package traits

trait A {
  val name: String
}

trait B {
  this: A => // this:混入類型 => [self type] 在特質中混入特質,所依賴的成員可以再沒有導入的情況下使用
  def sayHi = s"Hello:$name"
}

class C(val name: String) extends B with A {

}

object C {
  def main(args: Array[String]): Unit = {
    val c = new C("zs")
    println(c.sayHi)
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章