特质 - Trait
Scala做了一些面向对象的创新,其中之一就是特质(Trait)。trait类似于带有部分实现的抽象类。
一个特质(trait)代表一个接口,由相关类的层级所支持。它是一个抽象机制,帮助开发模块化、可重用和可扩展的代码。
从概念上来说,接口是通过一系列方法所定义的。Scala trait 与Java 的接口类似。不过Java 中的接口只包括方法签名,而一个Scala 的trait 可以包含方法的实现。此外,一个Scala trait 也可以包含字段。一个类可以重用在trait 中实现的这些字段和方法。
一个trait 看上去与一个抽象类相似。都包含字段和方法。关键的区别在于一个类只能从一个类继承,但它可以从任意数量的trait 继承。
下面是一个trait 的例子:
trait Shape{ def area(): Int } class Square(length:Int) extends Shape{ def area = length * length; } class Rectangle(length:Int, width:Int) extends Shape{ def area = length * width; } val square = new Square(10); val area = square.area;
在下面的代码中,定义了两个trait,并定义了实现这两个trait的类Batmobile:
// Trait(特质,类似于Java的接口) trait flying{ def fly() = println("flying") } trait gliding{ def float() = println("gliding") } // 实现了Trait接口的类 class Batmobile(speed:Int) extends Vehicle(speed) with flying with gliding{ override val mph:Int = speed override def race() = println("Racing Batmobile") override def fly() = println("Flying Batmobile") override def float() = println("Gliding Batmobile") } // 对象 val vehicle3 = new Batmobile(300) vehicle3.fly vehicle3.float val vehicleList = List(vehicle1, vehicle2, vehicle3) val fastestVehicle = vehicleList.maxBy(_.mph)
Scala特质说明:
- Scala中同样像Java一样不能实现多继承,只能通过多实现来弥补,同时多实现,要比多继承灵活的多。
- 在Java中的实现,我们称之为接口interface,使用关键字implement;在Scala中,我们称之为特质trait,使用关键字extends。
- 如果在Java中实现的是多个接口,接口之间使用","隔开;如果在Scala中继承的是多个特质,特质之间使用with隔开
- trait是一个比较特殊的结构,既可以有抽象方法,也可以有非抽象方法。如果trait中的方法都是抽象的,我们就可以将其看作Java的接口。
- 当我们扩展/继承的多个特质,拥有相同的方法的时候,默认只会调用最右面一个特质的方法。
下面的代码中,定义了一个特质log,它有一个抽象的方法log:
trait Log { def log(msg:String): Unit = { } } class ConsoleLog extends Log { override def log(msg: String): Unit = { println(s"将 ${msg} 输出到Console") } } trait FileLog extends Log { override def log(msg: String): Unit = { println(s"将 ${msg} 输出到File") super.log(msg) } } trait FlumeLog extends Log { override def log(msg: String): Unit = { println(s"将 ${msg} 采集到Flume") super.log(msg) } } class MainLog extends FileLog with FlumeLog { override def log(msg: String): Unit = { super.log(msg) } } // ====================================================================== val cLog:Log = new ConsoleLog cLog.log("日志信息") println("==================") val mLog:MainLog = new MainLog mLog.log("info")
下面是另一个简单的例子:
trait Animal trait FurryAnimal extends Animal case class Dog(name: String) extends Animal case class Cat(name: String) extends Animal val x = Array(Dog("Fido"), Cat("Felix")) val x = Array[Animal](Dog("Fido"), Cat("Felix"))
【示例】简单trait示例。
// 定义一个trait,提供DAO层的方法签名 trait DonutShoppingCartDao{ def add(donutName: String): Long def update(donutName: String): Boolean def search(donutName: String): String def delete(donutName: String): Boolean } // 创建一个DonutShoppingCart类,它扩展上面的trait并实现其方法 class DonutShoppingCart extends DonutShoppingCartDao { override def add(donutName: String): Long = { println(s"DonutShoppingCart -> add method -> donutName: $donutName") 1 } override def update(donutName: String): Boolean = { println(s"DonutShoppingCart -> update method -> donutName: $donutName") true } override def search(donutName: String): String = { println(s"DonutShoppingCart -> search method -> donutName: $donutName") donutName } override def delete(donutName: String): Boolean = { println(s"DonutShoppingCart -> delete method -> donutName: $donutName") true } } // 主类 object TraitDemo2 { def main(args: Array[String]): Unit = { // 现在我们可以创建DonutShoppingCart的实例 val donutShoppingCart1: DonutShoppingCart = new DonutShoppingCart() // 并调用相应的add、update、search和delete方法 donutShoppingCart1.add("Vanilla Donut") donutShoppingCart1.update("Vanilla Donut") donutShoppingCart1.search("Vanilla Donut") donutShoppingCart1.delete("Vanilla Donut") println("----------------------------------------------") // 因为我们的DonutShoppingCart类扩展了特性DonutShoppingCartDao, // 也可以将DonutShoppingCart对象的类型分配给trait DonutShoppingCartDao val donutShoppingCart2: DonutShoppingCartDao = new DonutShoppingCart() donutShoppingCart2.add("Vanilla Donut") donutShoppingCart2.update("Vanilla Donut") donutShoppingCart2.search("Vanilla Donut") donutShoppingCart2.delete("Vanilla Donut") } }
【示例】带类型参数的trait示例。
// 定义一个trait,提供DAO层的方法签名 trait DonutShoppingCartDao[A]{ def add(donut: A): Long def update(donut: A): Boolean def search(donut: A): A def delete(donut: A): Boolean } // 创建一个DonutShoppingCart类,它扩展上面的trait并实现其方法 class DonutShoppingCart[A] extends DonutShoppingCartDao[A] { override def add(donut: A): Long = { println(s"DonutShoppingCart -> add method -> donut: $donut") 1 } override def update(donut: A): Boolean = { println(s"DonutShoppingCart -> update method -> donut: $donut") true } override def search(donut: A): A = { println(s"DonutShoppingCart -> search method -> donut: $donut") donut } override def delete(donut: A): Boolean = { println(s"DonutShoppingCart -> delete method -> donut: $donut") true } } // 主类 object TraitDemo3 { def main(args: Array[String]): Unit = { // 现在我们可以创建DonutShoppingCart的实例 val donutShoppingCart1: DonutShoppingCart[String] = new DonutShoppingCart[String]() // 并调用相应的add、update、search和delete方法 donutShoppingCart1.add("Vanilla Donut") donutShoppingCart1.update("Vanilla Donut") donutShoppingCart1.search("Vanilla Donut") donutShoppingCart1.delete("Vanilla Donut") println("----------------------------------------------") // 因为我们的DonutShoppingCart类扩展了特性DonutShoppingCartDao, // 也可以将DonutShoppingCart对象的类型分配给trait DonutShoppingCartDao val donutShoppingCart2: DonutShoppingCartDao[String] = new DonutShoppingCart[String]() donutShoppingCart2.add("Vanilla Donut") donutShoppingCart2.update("Vanilla Donut") donutShoppingCart2.search("Vanilla Donut") donutShoppingCart2.delete("Vanilla Donut") } }
【示例】扩展自多个trait的示例。
// 定义一个trait,提供DAO层的方法签名 trait DonutShoppingCartDao[A]{ def add(donut: A): Long def update(donut: A): Boolean def search(donut: A): A def delete(donut: A): Boolean } // 创建第二个trait,它将定义检查商品库存的方法 trait DonutInventoryService[A] { def checkStockQuantity(donut: A): Int } // 创建一个DonutShoppingCart类,它扩展上面的多个trait并实现其方法 class DonutShoppingCart[A] extends DonutShoppingCartDao[A] with DonutInventoryService[A] { override def add(donut: A): Long = { println(s"DonutShoppingCart -> add method -> donut: $donut") 1 } override def update(donut: A): Boolean = { println(s"DonutShoppingCart -> update method -> donut: $donut") true } override def search(donut: A): A = { println(s"DonutShoppingCart -> search method -> donut: $donut") donut } override def delete(donut: A): Boolean = { println(s"DonutShoppingCart -> delete method -> donut: $donut") true } override def checkStockQuantity(donut: A): Int = { println(s"DonutShoppingCart -> checkStockQuantity method -> donut: $donut") 10 } } // 主类 object TraitDemo3 { def main(args: Array[String]): Unit = { // 现在我们可以创建DonutShoppingCart的实例 val donutShoppingCart: DonutShoppingCart[String] = new DonutShoppingCart[String]() // 并调用相应的add、update、search和delete方法 donutShoppingCart.add("Vanilla Donut") donutShoppingCart.update("Vanilla Donut") donutShoppingCart.search("Vanilla Donut") donutShoppingCart.delete("Vanilla Donut") donutShoppingCart.checkStockQuantity("Vanilla Donut") } }
【示例】使用标准Scala库来使用trait进行依赖注入,而无需导入任何第三方工具和库。
// 定义一个trait trait DonutDatabase[A] { def addOrUpdate(donut: A): Long def query(donut: A): A def delete(donut: A): Boolean } // 假设我们的存储层是Apache Cassandra。我们创建了一个名为CassandraDonutStore[A]的类, // 它将通过扩展trait DonutDatabase[A]来促进所有的CRUD操作。 class CassandraDonutStore[A] extends DonutDatabase[A] { override def addOrUpdate(donut: A): Long = { println(s"CassandraDonutDatabase -> addOrUpdate method -> donut: $donut") 1 } override def query(donut: A): A = { println(s"CassandraDonutDatabase -> query method -> donut: $donut") donut } override def delete(donut: A): Boolean = { println(s"CassandraDonutDatabase -> delete method -> donut: $donut") true } } // 创建一个trait,它将定义数据访问层的方法,并将要求DonutDatabase的依赖注入 // 接下来,我们创建一个名为DonutShoppingCartDao的trait,它将是执行CRUD操作的主要API。 // 为此,我们定义了一个类型DonutDatabase[A]的val,它将被依赖注入。 trait DonutShoppingCartDao[A] { val donutDatabase: DonutDatabase[A] // 依赖注入 def add(donut: A): Long = { println(s"DonutShoppingCartDao -> add method -> donut: $donut") donutDatabase.addOrUpdate(donut) } def update(donut: A): Boolean = { println(s"DonutShoppingCartDao -> update method -> donut: $donut") donutDatabase.addOrUpdate(donut) true } def search(donut: A): A = { println(s"DonutShoppingCartDao -> search method -> donut: $donut") donutDatabase.query(donut) } def delete(donut: A): Boolean = { println(s"DonutShoppingCartDao -> delete method -> donut: $donut") donutDatabase.delete(donut) } } // 创建一个trait,它将定义检查商品库存的方法,并且需要注入DonutDatabase依赖项 // 类似于前面的例子,我们创建了另一个trait,它将负责检查商品库存。 trait DonutInventoryService[A] { val donutDatabase: DonutDatabase[A] // 依赖注入 def checkStockQuantity(donut: A): Int = { println(s"DonutInventoryService-> checkStockQuantity method -> donut: $donut") donutDatabase.query(donut) 1 } } // 创建一个trait,它将作为一个facade,并扩展多个trait,即DonutShoppingCartDao和DonutInventoryService。 // 我们还注入了存储层的一个实现,在本例中是CassandraDonutStore的一个实例 trait DonutShoppingCartServices[A] extends DonutShoppingCartDao[A] with DonutInventoryService[A] { override val donutDatabase: DonutDatabase[A] = new CassandraDonutStore[A]() } // 创建一个DonutShoppingCart类,它扩展了一个名为DonutShoppingCartServices的外观, // 以公开DonutShoppingCart所需的所有底层trait class DonutShoppingCart[A] extends DonutShoppingCartServices[A] { } // 主类 object TraitDemo5 { def main(args: Array[String]): Unit = { // 创建DonutShoppingCart实例 val donutShoppingCart: DonutShoppingCart[String] = new DonutShoppingCart[String]() // 并调用add、update、search和delete方法 donutShoppingCart.add("Vanilla Donut") donutShoppingCart.update("Vanilla Donut") donutShoppingCart.search("Vanilla Donut") donutShoppingCart.delete("Vanilla Donut") } }