12 详解scalal中的特质(Traits)和混入的结合

详解scalal中的特质(Traits)

  • 我们基本的思路,先将官方的文档讲清楚,再结合特质和混入的用法,做结合的说明。

1-1 特质

  • 特质 (Traits) 用于在类 (Class)之间共享程序接口 (Interface)和字段 (Fields)。 它们类似于Java 8的接口。 类和对象 (Objects)可以扩展特质,但是特质不能被实例化,因此特质没有参数。

1-2 定义一个特质

  • 最简化的特质就是关键字trait+标识符
trait HairColor
  • 特征作为泛型类型和抽象方法非常有用。
trait Iterator[A] {
  def hasNext: Boolean
  def next(): A
}
  • 扩展 trait Iterator [A] 需要一个类型 A 和实现方法hasNext和next。

1-2 使用特质

  • 使用 extends 关键字来扩展特征。然后使用 override 关键字来实现trait里面的任何抽象成员
//官方的例子,讲的还是比较清楚的
trait Iterator[A] {
  def hasNext: Boolean
  def next(): A
}

class IntIterator(to: Int) extends Iterator[Int] {
  private var current = 0
  override def hasNext: Boolean = current < to
  override def next(): Int =  {
    if (hasNext) {
      val t = current
      current += 1
      t
    } else 0
  }
}


val iterator = new IntIterator(10)
iterator.next()  // returns 0
iterator.next()  // returns 1
  • 这个类 IntIterator 将参数 to 作为上限。它扩展了 Iterator [Int],这意味着方法 next 必须返回一个Int。

1-3 子类型

import scala.collection.mutable.ArrayBuffer

trait Pet {
  val name: String
}

class Cat(val name: String) extends Pet
class Dog(val name: String) extends Pet

val dog = new Dog("Harry")
val cat = new Cat("Sally")

val animals = ArrayBuffer.empty[Pet]
animals.append(dog)
animals.append(cat)
animals.foreach(pet => println(pet.name)) 
  • 在这里 trait Pet 有一个抽象字段 name ,name 由Cat和Dog的构造函数中实现。最后一行,我们能调用pet.name的前提是它必须在特质Pet的子类型中得到了实现。

1-4 特质总结

  • 基本上,上面的官方文档说明的还是比较的详细,讲特质的基本的用法,讲清楚了,但是,再实际的开发中,情况要复杂些。下面 ,我先将混入讲清楚,再结合起来说一下。他们的用法。

1-2 通过混入(MIXIN)来组合类

  • 当某个特质被用于组合类时,被称为混入。
abstract class A {
  val message: String
}
class B extends A {
  val message = "I'm an instance of class B"
}
trait C extends A {
  def loudMessage = message.toUpperCase()
}
class D extends B with C

val d = new D
println(d.message)  // I'm an instance of class B
println(d.loudMessage)  // I'M AN INSTANCE OF CLASS B
  • 类D有一个父类B和一个混入C。一个类只能有一个父类但是可以有多个混入(分别使用关键字extends和with)。混入和某个父类可能有相同的父类。
  • 下面例子,其中使用了抽象类:
abstract class AbsIterator {
  type T
  def hasNext: Boolean
  def next(): T
}
  • 该类中有一个抽象的类型T和标准的迭代器方法。
  • 接下来,我们将实现一个具体的类(所有的抽象成员T、hasNext和next都会被实现)
class StringIterator(s: String) extends AbsIterator {
  type T = Char
  private var i = 0
  def hasNext = i < s.length
  def next() = {
    val ch = s charAt i
    i += 1
    ch
  }
}
  • StringIterator带有一个String类型参数的构造器,可用于对字符串进行迭代。(例如查看一个字符串是否包含某个字符)
  • 现在我们创建一个特质,也继承于AbsIterator
trait RichIterator extends AbsIterator {
  def foreach(f: T => Unit): Unit = while (hasNext) f(next())
}
  • 该特质实现了foreach方法——只要还有元素可以迭代(while (hasNext)),就会一直对下个元素(next()) 调用传入的函数f: T => Unit。因为RichIterator是个特质,可以不必实现AbsIterator中的抽象成员。
  • 下面我们要把StringIterator和RichIterator 中的功能组合成一个类。
object StringIteratorTest extends App {
  class RichStringIter extends StringIterator("Scala") with RichIterator
  val richStringIter = new RichStringIter
  richStringIter foreach println
}
  • 新的类RichStringIter有一个父类StringIterator和一个混入RichIterator。如果是单一继承,我们将不会达到这样的灵活性。
  • 基本上我们讲官方文档上面的说清楚了。下面我们结合特质和混入说明下。两者之间的混合使用。

1-3 特质和混入的结合使用

1-3-1 特质的简单说明

  • java中的接口无法直接构建对象,必须使用实现类
  • Java中的接口是可以声明方法的.早期版本中的声明的方法,都是抽象的.但是新版本是可以默认实现的
  • java中的接口是可以声明属性的.属性值无法修改.
  • scala中的特质也无法构建对象
  • scala中的特质是可以执行代码的
  • scala中的特质也是无法创建对象的
  • scala特质中声明的属性和方法都是可以混入的类中调用的

1-3-2 多特质类的使用

object Scala15_Interface2 {
  def main(args: Array[String]): Unit = {
    new User15()
  }

}



trait TestTrait15{
  println("trait code ....")
}

trait Person15 extends TestTrait15{
  println("parent code ....")
}

class User15 extends Person15 with TestTrait15{
  println("child code ....")
}

trait code ....
parent code ....
child code ....
  • 特质和父类没有关系,只和当前混入的类有关系,所以,在调用时,父类先执行,然后当前的混入的特质在执行,然后当前的类在执行.
  • 如果父类混入的相同的的特质,那么特质的代码只会执行一遍

1-3-3 多特质类中方法使用

object Scala15_Interface3 {
  def main(args: Array[String]): Unit = {
    val mysql: Mysql = new Mysql()
    mysql.insert()
  }

}

trait Operate {
  def  insert(): Unit ={
    println("插入数据")
  }
}

trait DB extends Operate{
  override def insert(): Unit = {
    println("向数据库插入数据")
    super.insert()
  }
}


trait File  extends Operate{
  override def insert(): Unit = {
    println("向File插入数据")
   // super.insert()
    super[Operate].insert()
  }
}


class Mysql extends File with DB with Serializable{

}
  • 特质可以继承其他的特质
  • 特质重写父特质的方法
  • 多特质混入时,代码执行顺序为从左到右,如果有父特质,回优先执行
  • 特质中super关键字不是指代父特质,指代上一级特质
  • 如果希望super 指向父特质,需要增加特殊操作: super[特质]
  • java的接口可以再scala中当成特质来用

1-3-4 特质的动态混入

def main(args: Array[String]): Unit = {
    val mysql1 = new MysqlVo() with Operation
    mysql1.insert()
  }

}

// 特质继承类
trait Operation {

  def insert(): Unit = {
    println("插入数据")
  }
}

class MysqlVo {

}
  • 根据闭合原则,我们编码的时候,尽量不会为了业务的拓展,修改原来的代码,那么我们再scala 中可以使用动态的特质实现。
  • 动态特质,指的是,我们再不动原来的code,我们声明一个新的业务特质,再new的时候 with 特质就是可以了。这样我们new出来的类,就可以使用新特质的里面的东东了。
  • 在上面的演示例子中,MysqlVo在没有修改任何代码的情况下,在new的时候“ val mysql1 = new MysqlVo() with Operation”,增加了Operation这个特质,然后调用Operation特质中的“insert”方法。

1-3-5 指定特质的使用范围

object Scala15_Interface4 {
  def main(args: Array[String]): Unit = {
    val mysql1 = new Mysql18()
  }

}
// 特质继承类
trait exceptionHandler {
  this:Exception =>
  def  insert(): Unit ={
    println("插入数据")
    this.getMessage
  }
}

class Mysql18 extends Exception with exceptionHandler{

}
  • 上面code中指定了特质exceptionHandler使用的范围,就是才异常的情况下,我们才会使用,insert这个方法。
  • 这样有个好处,就是指定特质的使用范围。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值