参数限定(Parameter bounds)考虑这样一个方法:updateMax,他将一个cell的值设置为其当前值与一个给定值之间较大的那个。我们希望这个函数能够作用于所有的cell类型,只要其值类型能够按照一个特征(trait)Ordered定义的“<”操作符进行比较。目前假定这个特征定义如下:(更精确的定义在Scala标准类库中)
trait Ordered[T] {
def < (x: T): boolean
}
这样,updateMax方法可以通过如下方式进行泛型定义,其中使用到的方法称为限定多态(Bounded polymorphism):
def updateMax[T <: Ordered[T]](c: GenCell[T], x: T) =
if (c.get < x) c.set(x)
这里,类型参数定义子句[T <: Ordered[T]]引入了受限类型参数T,它限定参数类型T必须是Ordered[T]的子类型。这样,“<”操作符就可以应用于类型为T的参数了。同时,这个例子还展现出一个受限参数类型本身可以作为其限定类型的一部分,也就是说Scala支持F-受限多态(F-bounded polymorphism[10])。
协变性(Variance)泛型和子类型(subtyping)组合在一起产生这样一个问题:它们如何相互作用。如果C是一个类型构造子(type constructor),S是T的一个子类,那么C[S]是不是也是C[T]的子类呢?我们把有这种特性的类型构造子称为协变的(covariant)。可以看出GenCell这个类型构造子显然不是协变的,否则的话,下面这段代码就是合法的,但实际上它将会在运行时抛出错误:
val x: GenCell[String] = new GenCell[String]("abc")
val y: GenCell[Any] = x; // illegal!
y.set(1)
val z: String = y.get
GenCell中的可变(mutable)变量使其无法成为协变的。实际上,GenCell[String]不是GenCell[Any]的子类,因为有些可以针对GenCell[Any]的操作不能应用于GenCell[String],例如将其设置一个整型值。
另一方面,对于不可变数据类型,构造子的协变性是很自然成立的。例如:一个不可变的整数列表自然可以被看做是一个Any列表的特例。此外,在另一些情况下我们正好需要逆协变性(contravariance),例如一个输出管道Chan[T],有一个以T为类型参数的写操作,我们自然希望对于所有T<:S,都有Chan[S]<:Chan[T]。
Scala允许通过“+/-”定义类型参数的协变性,用“+”放在类型参数前表示构造子对于该参数是协变的,“-”则表示逆协变,没有任何符号则表示非协变。
下面的GenList定义了一个协变的列表,包含isEmpty、head和tail等三个方法。
abstract class GenList[+T] {
def isEmpty: boolean
def head: T
def tail: GenList[T]
}
Scala的类型系统通过跟踪类型参数的每一次使用来确保协变性确实成立。这些使用位置被分为几类:出现在不可变字段和方法返回结果被认为是协变的;出现在方法参数和类型参数的上/下界时被认为是逆协变的;非协变的类型参数永远出现在非协变的位置;在一个逆协变类型参数的内部,协变与逆协变是反转的。类型系统保证协变(逆协变)的类型参数总是出现在协变(逆协变)的位置上。(这段话叙述比较抽象,没有给出任何例子。由于是在说明Scala编译器在协变这个概念上的实现机制,不关心语言实现细节的话可以在一定程度上忽略——译注)
下面是GenList的两个实现:
object Empty extends GenList[Nothing] {
def isEmpty: boolean = true
def head: Nothing = throw new Error("Empty.head")
def tail: GenList[Nothing] = throw new Error("Empty.tail")
}
class Cons[+T](x: T, xs: GenList[T])
extends GenList[T] {
def isEmpty: boolean = false
def head: T = x
def tail: GenList[T] = xs
}
注意:Empty对象代表一个空列表,其元素可以是任何类型。这一点就是由协变性保证的,因为Empty的类型是GenList[Nothing],对于任何T而言,它都是GenList[T]的子类型。
分享到:
相关推荐
- **1.1 函数式编程与面向对象编程的结合**:本节探讨了Scala如何将函数式编程和面向对象编程完美地融合在一起,使其成为一种强大的多范式编程语言。 - **1.2 静态类型系统**:介绍Scala的静态类型系统是如何工作...
#### 一、SparkSQL概述 **1.1 什么是SparkSQL** SparkSQL是Apache Spark框架中的一个重要组件,主要用于处理结构化的数据。它为开发者提供了两个关键的功能:DataFrame API 和 SQL 查询语言的支持。DataFrame API ...
**1.1 Spark概述** - **定义**: Apache Spark 是一种高性能、通用的集群计算平台,主要用于处理大规模数据集。 - **适用场景**: 包括批处理、迭代算法、交互式查询、流处理等多种分布式计算场景。 **1.2 大一统的...
#### 1 概述 ##### 1.1 前言 随着计算机硬件技术和网络技术的快速发展,计算机拥有的核心数量不断增多,分布式技术和集群技术的成熟使得一个应用程序可以被分解运行在多台独立的计算机上,这些计算机可能运行着...
1.1 **什么是Spark SQL** Spark SQL是Apache Spark的一个核心组件,它集成了SQL查询和Spark的分布式计算能力,使得开发人员可以使用SQL或者DataFrame和DataSet API对大规模数据进行查询和分析。Spark SQL不仅支持...
#### 1.1 Spark概述 Apache Spark是一种开源的大规模数据处理框架,它提供了一个比Hadoop MapReduce更为高级且易用的API,能够支持多种计算模式,包括批处理、交互式查询、流处理等。Spark的核心特性在于其内存计算...
#### 一、Spark概述 **1.1 什么是Spark** Spark是一种高性能、多功能且可扩展的大数据分析处理引擎。它最初由加州大学伯克利分校的AMPLab研发,于2009年问世,并在2010年开源。随着Spark社区的不断发展,它在2013...
**1.1 Hadoop概述** Hadoop 是一个开源框架,它为大规模数据集提供存储和处理的能力,主要由以下几个核心组件构成: - **Hadoop Distributed File System (HDFS)**:这是一个分布式文件系统,其设计目标是在廉价...
1.1 选题依据和研究意义 随着人工智能技术的快速发展,机器学习,特别是深度学习,已经成为解决复杂问题的关键工具。手写识别作为自然交互的一个重要环节,广泛应用于移动设备、银行支票自动处理、邮政编码识别等...
Kotlin不断更新以引入新特性,例如在1.1版本中增加了协程的早期实验支持,1.2版本中进一步优化了协程,同时引入了密封类和非空数组等特性,提升了语言的表达能力和安全性。 【基本语法和习惯用法】 Kotlin的基础...
#### 一、SparkSQL概述 ##### 1.1 什么是SparkSQL SparkSQL是Apache Spark框架中的一个核心组件,主要用于处理结构化的数据。它提供了一个名为DataFrame的高级抽象,并且能够作为一个分布式SQL查询引擎来运行。与...
- **1.1 Spark简介与发展** - **背景**:随着大数据处理需求的增长,传统的Hadoop MapReduce框架虽然提供了强大的计算能力,但在处理速度、易用性和灵活性方面存在一定的局限性。 - **Spark的诞生**:Spark作为一...
- **接口**: 类似于Java中的接口(interface),用于定义行为的抽象模板。 - **可见性修饰符**: 如public、private、protected等,用于控制成员的访问级别。 - **扩展**: Kotlin独特的功能之一,允许向现有类添加新...
##### 1.1 SparkSQL的发展历程 **1.1.1 Hive与Shark** 在大数据处理领域,Hadoop的出现标志着分布式计算的一个新阶段。然而,传统的MapReduce编程模型对于没有深入理解分布式计算原理的用户来说,存在一定的学习...