博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【Scala之旅】特质与高级类型
阅读量:6212 次
发布时间:2019-06-21

本文共 4104 字,大约阅读时间需要 13 分钟。

本节翻译自

综述:在本节中,你将学会如何使用特质;以及抽象类型、自身类型和复合类型这几个高级类型。

特质

特质用于在类之间共享接口和字段。它们类似于Java 8的接口。类和对象可以扩展特征,但是特质不能被实例化,因此没有参数。

定义特质

一个最小的特质就是关键字 trait 和标识符:

trait HairColor

作为泛型类型和抽象方法,特质显得特别有用。

trait Iterator[A] {  def hasNext: Boolean  def next(): A}

扩展 trait Iterator[A] 需要类型 A 还有方法 hasNextnext 的实现。

使用特质

使用关键字 extends 可以扩展特质。然后使用关键字 override 来实现该特质里的任何抽象成员。

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()  // prints 0iterator.next()  // prints 1

这个 IntIterator 类将一个参数 to 作为一个上界。它扩展了 Iterator[Int],这意味着方法 next 必须返回一个 Int。

子类型

可以在需要特征的地方使用子类型。

import scala.collection.mutable.ArrayBuffertrait Pet {  val name: String}class Cat(val name: String) extends Petclass Dog(val name: String) extends Petval 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))  // Prints Harry Sally

trait Pet 有一个抽象的字段 name,它在构造函数中由 CatDog 实现。在最后一行,我们调用 pet.name,该名称必须在特质 Pet 的任何子类型中实现。

抽象类型

特质和抽象类可以有一个抽象类型的成员。这意味着具体的实现定义了实际的类型。这里有一个例子:

trait Buffer {  type T  val element: T}

这里我们定义了一个抽象类型 type T。它被用来描述 element。我们可以在抽象类中扩展这个特性,并向 T 添加一个上类型边界绑定,以使其更具体。

abstract class SeqBuffer extends Buffer {  type U  type T <: Seq[U]  def length = element.length}

请注意我们是如何使用另一个抽象类型 type U 作为一个上类型绑定:这个 SeqBuffer 类允许我们只在 Buffer 中存储序列,它声明 type T 必须是 Seq U 的子类型,用于新的抽象类型 U

带有抽象类型成员的特质或经常与匿名类实例化相结合使用。为了说明这一点,我们现在来看一个程序,它处理一个 SeqBuffer,引用了一个 Int 列表:

abstract class IntSeqBuffer extends SeqBuffer {  type U = Int}def newIntSeqBuf(elem1: Int, elem2: Int): IntSeqBuffer =  new IntSeqBuffer {       type T = List[U]       val element = List(elem1, elem2)     }val buf = newIntSeqBuf(7, 8)println("length = " + buf.length)println("content = " + buf.element)

这里有一个使用匿名类 IntSeqBuf 实现的工厂 newIntSeqBuf(即 new IntSeqBuffer),它将 type T 设置为一个 List[Int]

也可以将抽象类型成员转换为类的类型参数,反之亦然。下面是上述代码的一个版本,它只使用类型参数:

abstract class Buffer[+T] {  val element: T}abstract class SeqBuffer[U, +T <: Seq[U]] extends Buffer[T] {  def length = element.length}def newIntSeqBuf(e1: Int, e2: Int): SeqBuffer[Int, Seq[Int]] =  new SeqBuffer[Int, List[Int]] {    val element = List(e1, e2)  }val buf = newIntSeqBuf(7, 8)println("length = " + buf.length)println("content = " + buf.element)

注意,我们必须在这里使用型变注解(+T <: Seq[U]),以隐藏从方法 newIntSeqBuf 返回的对象的具体序列实现类型。此外,有些情况下,用类型参数代替抽象类型是不可能的。

自身类型

自身类型是一种声明一种特质必须被混入到另一种特质的方式,尽管它并没有直接地扩展它。这使得依赖项的成员可以不用导入。

自身类型是小写 this 或别名 this 的另一个标识符的类型的一种方式。语法看起来像正常的函数语法,但表示完全不同的语法。

要在一个特质中使用自我类型,编写一个标识符,将另一个特质混入在一起,以及一个 =>(例如 someIdentifier: SomeOtherTrait =>)。

trait User {  def username: String}trait Tweeter {  this: User =>  // reassign this  def tweet(tweetText: String) = println(s"$username: $tweetText")}class VerifiedTweeter(val username_ : String) extends Tweeter with User {  // We mixin User because Tweeter required it  def username = s"real $username_"}val realBeyoncé = new VerifiedTweeter("Beyoncé")realBeyoncé.tweet("Just spilled my glass of lemonade")  // prints "real Beyoncé: Just spilled my glass of lemonade"

因为我们说 this: User =>trait Tweeter,现在变量 username 适用于 tweet 方法。这也意味着 VerofoedTweeter 扩展了 Tweeter,它还必须混入 User(使用 with User)。

复合类型

有时,有必要表示对象的类型是其他类型的子类型。在Scala中,这可以通过复合类型 的帮助来表示,这些类型是对象类型的交集。

假设我们有两个特质 CloneableResetable

trait Cloneable extends java.lang.Cloneable {  override def clone(): Cloneable = {    super.clone().asInstanceOf[Cloneable]  }}trait Resetable {  def reset: Unit}

现在假设我们要写一个函数 cloneAndReset,它取出一个对象,克隆它并重新设置原始对象:

def cloneAndReset(obj: ?): Cloneable = {  val cloned = obj.clone()  obj.reset  cloned}

问题是,参数 obj 的类型是什么。如果它是 Cloneable 则对象可以被克隆但不可以被重置。如果它是 Resetable 我们可以重置对象但却无法进行克隆操作。为了避免这种情况下的类型转换,我们可以指定类型 obj 同时是 CloneableResetable。这种复合类型在Scala中是这样写的:Cloneable with Resetable

这是更新的函数:

def cloneAndReset(obj: Cloneable with Resetable): Cloneable = {  //...}

复合类型可以由几个对象类型组成,它们可能有一个单独的细化,可以用来缩小现有对象成员的签名。一般的形式是:A with B with C ... { refinement }

在中给出了细化的例子。

转载地址:http://rxdja.baihongyu.com/

你可能感兴趣的文章
Logstash 参考指南(作为Debian或RPM上的服务运行Logstash)
查看>>
leetCode 4 Median of Two Sorted Arrays
查看>>
xshell免费版下载安装
查看>>
基于moment写一个滑动日历
查看>>
移动端:对高度自适应的输入框说不~
查看>>
android开源新闻小程序、3D翻转公告效果、小说检索、Kotlin开发TODO清单等源码...
查看>>
PHP算法之判断是否是质数
查看>>
Android 系统开发_核心机制篇 -- 深入钻研 Handler(用法)
查看>>
promise学习(1)
查看>>
幂等的实现方案
查看>>
认识Java泛型
查看>>
node thread.sleep实现
查看>>
H5剪切板功能
查看>>
Spring Boot 参考指南(消息传递)
查看>>
如何在 Linux 上通过 C API 判断给定的 fd 的类型?
查看>>
Linux(CentOS)软件管理(2)- yum 在线安装
查看>>
重新定义数据库的时刻,阿里云数据库专家带你了解POLARDB
查看>>
【跃迁之路】【469天】刻意练习系列228(2018.05.20)
查看>>
如何修复UITextField在iOS10文字下沉问题
查看>>
一个简单的 laravel5 + vue 单页面博客
查看>>