Scala教程之:静态类型(二)

简介: Scala教程之:静态类型(二)

类型上界


像T <: A这样声明的类型上界表示类型变量T应该是类型A的子类。下面是个例子:


abstract class Animal {
 def name: String
}
abstract class Pet extends Animal {}
class Cat extends Pet {
  override def name: String = "Cat"
}
class Dog extends Pet {
  override def name: String = "Dog"
}
class Lion extends Animal {
  override def name: String = "Lion"
}
class PetContainer[P <: Pet](p: P) {
  def pet: P = p
}
val dogContainer = new PetContainer[Dog](new Dog)
val catContainer = new PetContainer[Cat](new Cat)
// this would not compile
val lionContainer = new PetContainer[Lion](new Lion)


类PetContainer接受一个必须是Pet子类的类型参数P。因为Dog和Cat都是Pet的子类,所以可以构造PetContainer[Dog]和PetContainer[Cat]。但在尝试构造PetContainer[Lion]的时候会得到下面的错误信息:


type arguments [Lion] do not conform to class PetContainer's type parameter bounds [P <: Pet]

这是因为Lion并不是Pet的子类。


类型下界


类型下界和类型上界相反,B >: A 表示类型参数 B 或抽象类型 B 是类型 A 的超类型。

下面看个它使用的例子:


trait Node[+B] {
  def prepend(elem: B): Node[B]
}
case class ListNode[+B](h: B, t: Node[B]) extends Node[B] {
  def prepend(elem: B): ListNode[B] = ListNode(elem, this)
  def head: B = h
  def tail: Node[B] = t
}
case class Nil[+B]() extends Node[B] {
  def prepend(elem: B): ListNode[B] = ListNode(elem, this)
}


这个例子会在编译的时候报错。因为方法 prepend 中的参数 elem 是协变的 B 类型。

在scala中函数的参数类型是逆变的,而返回类型是协变的。


要解决这个问题,我们需要将方法 prepend 的参数 elem 的型变翻转。 我们通过引入一个新的类型参数 U 来实现这一点,该参数具有 B 作为类型下界。


trait Node[+B] {
  def prepend[U >: B](elem: U): Node[U]
}
case class ListNode[+B](h: B, t: Node[B]) extends Node[B] {
  def prepend[U >: B](elem: U): ListNode[U] = ListNode(elem, this)
  def head: B = h
  def tail: Node[B] = t
}
case class Nil[+B]() extends Node[B] {
  def prepend[U >: B](elem: U): ListNode[U] = ListNode(elem, this)
}


这样就可以了。


内部类


内部类就是class里面的class,在java里面,内部类被看成是外部类的成员。但是在scala中内部类是和外部类的对象进行绑定的。这意味着即使是同一个外部类的不同对象,其包含的内部类是不同类型的。 我们举个例子:


class Graph {
  class Node {
    var connectedNodes: List[Node] = Nil
    def connectTo(node: Node) {
      if (!connectedNodes.exists(node.equals)) {
        connectedNodes = node :: connectedNodes
      }
    }
  }
  var nodes: List[Node] = Nil
  def newNode: Node = {
    val res = new Node
    nodes = res :: nodes
    res
  }
}


这里connectedNodes 中存储的所有节点必须使用同一个 Graph 的实例对象的 newNode 方法来创建。


val graph1: Graph = new Graph
val node1: graph1.Node = graph1.newNode
val node2: graph1.Node = graph1.newNode
val node3: graph1.Node = graph1.newNode
node1.connectTo(node2)
node3.connectTo(node1)


这里三个node都是graph1.Node类型。如果是非graph1.Node类型则无法编译成功。


val graph1: Graph = new Graph
val node1: graph1.Node = graph1.newNode
val node2: graph1.Node = graph1.newNode
node1.connectTo(node2)      // legal
val graph2: Graph = new Graph
val node3: graph2.Node = graph2.newNode
node1.connectTo(node3)      // illegal!


那如果想达到和java中内部内中一样的效果,不区分路径该怎么办呢?使用Graph#Node即可。


class Graph {
  class Node {
    var connectedNodes: List[Graph#Node] = Nil
    def connectTo(node: Graph#Node) {
      if (!connectedNodes.exists(node.equals)) {
        connectedNodes = node :: connectedNodes
      }
    }
  }
  var nodes: List[Node] = Nil
  def newNode: Node = {
    val res = new Node
    nodes = res :: nodes
    res
  }
}


抽象类型


抽象类型通常用T来表示,用在特质和抽象类中,表示实际类型可以由具体的实现类来确认:


trait Buffer {
  type T
  val element: T
}


通过抽象类来扩展这个特质后,就可以添加一个类型上边界来让抽象类型T变得更加具体。


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


复合类型


复合类型很简单,表示多个类型的交集,用with来表示。


假设我们定义了两个traits:


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


假如我们需要接受一个对象它即可以是Cloneable又可以是Resetable,那么可以这样定义:


def cloneAndReset(obj: Cloneable with Resetable): Cloneable = {
  //...
}
相关文章
|
Java Scala
Scala 教程
Scala 教程
198 2
|
IDE Java 编译器
scala的两种变量类型 var 和 val
scala的两种变量类型 var 和 val
391 2
scala的两种变量类型 var 和 val
|
安全 Java 大数据
大数据开发基础的编程语言的Scala的类型系统
Scala是一种强类型的编程语言,它具有一套完善的类型系统。本文将介绍Scala的类型系统,帮助开发者了解这门语言的类型安全性和灵活性。
176 0
|
分布式计算 Java Scala
一天学完spark的Scala基础语法教程十三、文件IO操作(idea版本)
一天学完spark的Scala基础语法教程十三、文件IO操作(idea版本)
207 0
一天学完spark的Scala基础语法教程十三、文件IO操作(idea版本)
|
分布式计算 Java Scala
一天学完spark的Scala基础语法教程十二、异常处理(idea版本)
一天学完spark的Scala基础语法教程十二、异常处理(idea版本)
297 0
一天学完spark的Scala基础语法教程十二、异常处理(idea版本)
|
分布式计算 Java Scala
一天学完spark的Scala基础语法教程十一、正则表达式(idea版本)
一天学完spark的Scala基础语法教程十一、正则表达式(idea版本)
255 0
一天学完spark的Scala基础语法教程十一、正则表达式(idea版本)
|
存储 分布式计算 Java
一天学完spark的Scala基础语法教程十、类和对象(idea版本)
一天学完spark的Scala基础语法教程十、类和对象(idea版本)
217 0
一天学完spark的Scala基础语法教程十、类和对象(idea版本)
|
分布式计算 Scala Spark
一天学完spark的Scala基础语法教程九、迭代器(idea版本)
一天学完spark的Scala基础语法教程九、迭代器(idea版本)
200 0
一天学完spark的Scala基础语法教程九、迭代器(idea版本)
|
分布式计算 Scala Spark
一天学完spark的Scala基础语法教程八、集合(idea版本)
一天学完spark的Scala基础语法教程八、集合(idea版本)
208 0
一天学完spark的Scala基础语法教程八、集合(idea版本)
|
大数据 Java 编译器
Scala 字符类型|学习笔记
快速学习 Scala 字符类型。
269 0