scala-19 高级类型

  • 单例类型( singleton type )可用于方法串接和带对象参数的方法
  • 类型投影( type projection )对所有外部类的对象都包含了其内部类的实例
  • 类型别名( type alias )给类型指定一个短小的名称
  • 结构类型( structural type )等效于“鸭子类型( duck typing )”
  • 存在类型( existential type )为泛型类型的通配参数提供了统一形式
  • 使用自身类型( self type )来表明某特质对混入它的类或对象的类型要求
  • “蛋糕模式 ( cake pa忧em )”用自身类型来实现依赖注入
  • 抽象类型( abstract type )必须在子类中被具体化
  • 高等类型( higher-kinded type )带有本身为参数化类型的类型参数

单例类型

  • 给定任何引用v ,你可以得到类型v.type ,它有两个可能的值: v和null 。 这昕上
    去像是一个挺古怪的类型,但它在有些时候很有用
  • 返回this的方法,可以把调用串接起来,例如 book.setAuthor(“Cay Fostman”).setTitle(“Quick Scala”)
class Document{
  private var title:String ="Null"
  private var author:String ="Null"
  def setTitle(title:String):Document={
    this.title=title
    this
  }
  def setAuthor(author:String):Document={
    this.author = author
    this
  }

  override def toString: String = s"title:${title},author:${author}"
}

val book = new Document
book.setAuthor("Cay Fostman").setTitle("Quick Scala")
  • 如果有个子类的话,由于 setTitle返回的是this, Scala将返回类型推断为Document 。 但 Document并没有addChapter方法 。
class Book extends Document{
  private var chapter = "Null"
  def addChapter(chp:String):Book={
    this.chapter=chp
    this
  }

  override def toString: String = super.toString + s",chpter:${chapter}"
}
  • 解决方法是声明setTitle的返回类型为this.type ,在父类的方法的返回类型设置为this.type
class Document{
  private var title:String ="Null"
  private var author:String ="Null"
  def setTitle(title:String):this.type ={
    this.title=title
    this
  }
  def setAuthor(author:String):this.type ={
    this.author = author
    this
  }

  override def toString: String = s"title:${title},author:${author}"
}
class Book extends Document{
  private var chapter = "Null"
  def addChapter(chp:String):Book={
    this.chapter=chp
    this
  }

  override def toString: String = super.toString + s",chpter:${chapter}"
}

//  使用
val b = new Book
b.setAuthor("fdf").setTitle("f").addChapter("df")
  • 如果你想要定义一个接受object实例作为参数的方法,可以使用单例类型 ,对象.type,不能直接写Title,这样是对象,不是类型
object Title
class Document {
  private var useNextArgAs: Any= null
  def set(obj : Title.type ): this.type= { useNextArgAs =obj ; this }
  def to(arg : String) =if (useNextArgAs ==Title) title= arg ; else
}
//使用
val book = new Document
book.set(Title).to ("Scala for the Impatient")

类型投影

class Network{
  class Member(val name:String){
    val contacts=new ArrayBuffer[Network#Member]
  }
}

路径

  • 表达式com.horstmann. impatient.chatter.Member
    或者,如果我们将Member嵌套在伴生对象当中的话,com.horstmann.impatient.Network.Member,这样的表达式称为路径
  • 在最后的类型之前,路径的所有组成部分都必须是“稳定的”,也就是说,它必须指定到单个、有穷的范围 。 组成部分必须是以下当中的一种 :
    – 包
    – 对象
    – val
    – this,super,super[S],C.this,C.super,C.super[S]
  • 路径的组成部分不能是类,嵌套的内部类不是单个类型,而是每个实例都有各自的一套类型,爷爷不能是var类型的
  • 在内部,编译器将所有嵌套的类型表达式a.b.c.T都翻译成类型投影a.b.c.type#T。举例来说,chatter.Member就成为chatter.type#Member任何位于chatter.type单例中的Member。这不是你通常需要担心的问题。不过,有时候你会看到关于类型a.b.c.type#T的报错信息。将
    它翻译回a.b.c.T即可

类型别名

  • 对于复杂类型,可以用type关键字创建一个简单的别名
object Book1{
  type Index1 = scala.collection.mutable.ArrayBuffer[Int]
}

val hm:Index1 = new Book1.Index1()
hm.append(10)
println(hm)

type i = Int
val a:i =10 
  • 类型别名必须被嵌套在类或对象中 。 它不能出现在 Sca la文件的顶层
  • type关键字同样被用于那些在子类中被具体化的抽象类型
abstract class Reader {
  type Contents
  def read(fileName: String): Contents
}

结构类型

  • 结构类型指的是一组关于抽象方法 、字段和类型的规格说明,这些抽象方法、字段和类型是满足该规格的类型必须具备的
def appendLines (target: { def append(str : String): Any },
                 lines: Iterable [String] ) {
  for (l <- lines) {
    target.append(l); target.append("\n")
  }
}
  • 你可以对任何具备append方法的类的实例调用appendLines方法 。 这比定义一个
    Appendable特质更为灵活,因为你可能并不总是能够将该特质添加到使用的类上 。在背后, Scala使用反射来调用 target,append(…)。 结构类型让你可以安全而方便地做这样的反射调用
  • 相比常规方法调用,反射调用的开销要大得多 。 因此,你应该只在需要抓住那些无法共享一个特质的类的共通行为的时候才使用结构类型
  • 结构类型与诸如JavaScript或Ruby这样的动态类型编程语言中的“鸭子类型”很相似。在这些语言当 中,变量没有类型。当你写下obj . quack()的时候 , 运行时会去检查obj 指向 的特定对象在那一刻是否具备quack方法。换句话说 ,你无须将obj 声明为 Duck (鸭子),只要它走起路来以及嘎嘎叫起来像一只鸭子即可。

复合类型

  • 复合类型的定义形式如下:T1 with T2 with T3 …
    其中,T1 T2 T3 等是类型。要想成为该复合类型的实例,某个值必须满足每一个类型的要求才行。因此,这样的类型也被称作交集类型。
    你可以用复合类型来操纵那些必须提供多个特质的值。例如:
    val image=newArrayBuffer[java.awt.Shape with java.io.Serializable] 只能添加那些既是形状( Shape )也是可被序列化的对象
  • 你可以把结构类型的声明添加到简单类型或复合类型 。 例如:
    Shape with Serializable { def contains(p : Point): Boolean )。该类型的实例必须既是Shape 的子类型也是Serializable的子类型,并且必须有一个带Point参数的contain s方法

从技术上讲,如下结构类型
{def append(str: String): Any }
是如下代码的简写 :
AnyRef { def append(str : String) : Any}
而复合类型
Shape with Serializable
是以下代码的简写 :
Shape with Serializable {)

中置类型

  • 中置类型是一个带有两个类型参数的类型,以“中置”语法表示,类型名称写在两个类型参数之间
    type ×[A,B] = (A,B)
    val ab :String × Int = ("sky",4)
  • 所有中置类型操作符都拥有相同的优先级 。 和常规操作符一样,它们是左结合的一一除非它们的名称以:结尾
  • 中置类型的名称可以是任何操作符字符的序列(除单个*外)。这个规则是为了避免与变长参数声明T*混淆。

存在类型

  • 存在类型的定义方式是在类型表达式之后跟上 forSome { … },花括号中包含了 type和val的声明 。例如:Array [T] forSome ( type T < : JComponent }
    上述代码和你在第 18章中看到的类型通配符效果是一样的:
    Array [_ <: JComponent] 。
  • Scala的类型通配符只不过是存在类型 的“语法糖” 。 例如:
    Array[ _ ]等同于Array[T] forSome { type T }
    而Map[_,_]等同于Map[T, U] forSome { type T; type U }
  • 更复杂一点的Map[T , U] forSome { type T; type U <: T }
  • 该方法将会接受相同网络的成员,但拒绝那些来自不同网络的成员
    def process[M <: n.Member forSome { val n: Network }] (m1: M, m2: M)= (m1, m2)
    在这里插入图片描述
  • 要不带警告地使用存在类型,你必须引人 scala.language.existentials

Scala类型系统

在这里插入图片描述
在这里插入图片描述

自身类型

  • 特质可以要求混入它的类扩展自另一个类型 。 你用自身类型( self type )的声明来定义特质:this: 类型A =>这样的特质只能被混入给定类型A的子类当中
trait Logged {
  def log(msg: String)
} 
trait LoggedException extends Logged {
  this: Exception =>
  def log() { log(getMessage()) }
  // OK to call getMessage because this is an Exception
}
  • 多个类型要求
this: T with U with ... =>
  • 自身类型并不会自动继承,需要重复声明自身类型
trait ManagedException extends LoggedException {
	this: Exception =>
	...
}

依赖注入

抽象类型

  • 类或特质可以定义一个在子类中实现的抽象类型
  • 在特质Reader中,抽象类型Contents
import java.awt.image.BufferedImage
import java.io.File
import javax.imageio.ImageIO
import scala.io.Source

trait Reader{
  type Contents
  def read(fileName:String):Contents
}
class StringReader extends Reader{
  override type Contents = String
  
  override def read(fileName: String): Contents = {
    Source.fromFile(fileName).mkString
  }
}
class ImageReader extends Reader{
  override type Contents = BufferedImage

  override def read(fileName: String): Contents = {
    ImageIO.read(new File(fileName))
  }
}
  • 同样的效果可以通过类型参数实现
trait Reader[C]{
  def read(fileName:String):C
}
class StringReader extends Reader[String]{
  override def read(fileName: String): String = Source.fromFile(fileName).mkString
}
class ImageReader extends Reader[BufferedImage]{
  override def read(fileName: String): BufferedImage = ImageIO.read(new File(fileName))
}
  • 如果类型是在类被实例化时给出的,则使用类型参数 。如果类型是在子类中给出的,则使用抽象类型。
  • 在构建子类时给出类型参数并没有什么不好。但是当有多个类型依赖时,抽象类型用起来更方便,可以避免使用一长串类型参数。
  • 抽象类型还能够描述类型间那些微妙的相互依赖。抽象类型可以有类型界定,这就和类型参数一样,例如type Event <: EventObject

家族多态

高等类型

  • 这两个有点难理解,后面有机会再理解一下
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

独孤尚亮dugushangliang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值