我正在读A Tour of Scala: Abstract Types . 什么时候使用抽象类型更好?
例如,
abstract class Buffer {
type T
val element: T
}
而是那些泛型,例如,
abstract class Buffer[T] {
val element: T
}
我正在读A Tour of Scala: Abstract Types . 什么时候使用抽象类型更好?
例如,
abstract class Buffer {
type T
val element: T
}
而是那些泛型,例如,
abstract class Buffer[T] {
val element: T
}
3 回答
您可以将抽象类型与类型参数结合使用来 Build 自定义模板 .
我们假设您需要 Build 一个具有三个连接特征的模式:
在类型参数中提到的参数的方式是AA,BB,CC本身
您可能会附带某种代码:
由于类型参数键,这不会以这种简单的方式工作 . 你需要使它协变才能正确继承
这个样本会编译,但它对方差规则设置了很强的要求,在某些情况下不能使用
编译器将使用一组方差检查错误进行对象
在这种情况下,您可以在其他特征中收集所有类型要求,并在其上参数化其他特征
现在我们可以为所描述的模式编写具体的表示,在所有类中定义left和join方法,并免费获得right和double
因此,抽象类型和类型参数都用于创建抽象 . 他们都有弱点和强点 . 抽象类型更具体,能够描述任何类型结构,但是冗长且需要明确指定 . 类型参数可以立即创建一堆类型,但会让您更加担心继承和类型边界 .
它们彼此协同作用,可以结合使用来创建复杂的抽象,这些抽象只能用其中一个来表达 .
我在读斯卡拉时有同样的问题 .
使用泛型的优点是您正在创建一系列类型 . 没有人需要子类
Buffer
- 他们只能使用Buffer[Any]
,Buffer[String]
等 .如果使用抽象类型,那么人们将被迫创建子类 . 人们将需要像
AnyBuffer
,StringBuffer
等类 .您需要确定哪个更适合您的特定需求 .
你对这个问题有一个很好的观点:
The Purpose of Scala's Type System
与Martin Odersky的对话,第三部分
Bill Venners和Frank Sommers(2009年5月18日)
更新(2009年10月):Bill Venners在这篇新文章中实际说明了以下内容:
Abstract Type Members versus Generic Type Parameters in Scala(见末尾摘要)
(这是第一次采访的相关摘录,2009年5月,强调我的)
一般原则
总有两种抽象概念:
参数化和
抽象成员 .
在Java中你也有两者,但它取决于你抽象的东西 .
在Java中,您有抽象方法,但不能将方法作为参数传递 .
您没有抽象字段,但可以将值作为参数传递 .
同样,您没有抽象类型成员,但您可以将类型指定为参数 .
所以在Java中你也有这三个,但是你可以用什么抽象原理来区分什么样的东西 . 你可以说这种区别是相当武断的 .
Scala方式
我们决定拥有 same construction principles for all three sorts of members .
所以你可以有抽象字段和值参数 .
您可以将方法(或"functions")作为参数传递,也可以对它们进行抽象 .
您可以将类型指定为参数,也可以对它们进行抽象 .
我们从概念上得到的是我们可以用另一个来模拟一个 . 至少在原理上,我们可以将各种参数化表达为面向对象的抽象形式 . 所以从某种意义上说,你可以说Scala是一种更正交和完整的语言 .
为什么?
特别是之前我们谈过的是什么?
长期存在的一个标准问题是动物和食物的问题 .
这个难题是有一个
Animal
类的方法,eat
,吃一些食物 .问题是如果我们继承Animal并且有一个像Cow这样的类,那么他们只吃Grass而不是任意食物 . 例如,牛不能吃鱼 .
你想要的是能够说牛有吃法只吃草而不吃其他东西 .
实际上,你不能用Java做到这一点,因为事实证明你可以构建不健全的情况,比如将Fruit分配给我之前谈过的Apple变量的问题 .
答案是 you add an abstract type into the Animal class .
你说,我的新Animal类有一种
SuitableFood
,我不知道 .所以's an abstract type. You don't给出了该类型的实现 . 然后你有
eat
方法只吃SuitableFood
.然后在
Cow
课程中我会说,好吧,我有一个Cow,它扩展了类Animal
,以及Cow type SuitableFood equals Grass
.所以 abstract types provide this notion of a type in a superclass that I don't know, which I then fill in later in subclasses with something I do know .
与参数化相同?
的确,你可以 . 你可以用它吃的食物参数化动物类 .
但 in practice, when you do that with many different things, it leads to an explosion of parameters ,通常,更多,在 bounds of parameters .
在1998年的ECOOP上,Kim Bruce,Phil Wadler和我有一篇论文,我们展示了 as you increase the number of things you don't know, the typical program will grow quadratically .
所以有非常好的理由不做参数,而是拥有这些抽象成员,因为他们不会给你这个二次爆炸 .
thatismatt在评论中提问:
我不确定使用抽象类型或泛型之间的关系是不同的 . 不同的是:
如何使用它们,以及
如何管理参数边界 .
要了解马丁所说的“参数爆炸,通常是什么,更多,在 bounds of parameters ", and its subsequent quadratically growth when abstract type are modeled using generics, you can consider the paper " Scalable Component Abstraction ”由马丁奥德斯基和马蒂亚斯曾格在2005年OOPSLA撰写,引用于publications of the project Palcom(2007年完成) ) .
相关摘录
定义
(注意:已经为面向对象语言提出了族多态性,作为支持可重用但类型安全的相互递归类的解决方案 .
家庭多态性的一个关键思想是家庭的概念,它们用于对相互递归的类进行分组)
有界类型抽象
(请注意,Peter Canning,William Cook,Walter Hill,Walter Olthoff论文:
Cardelli和Wegner引入了有限量化作为键入函数的方法,这些函数在给定类型的所有子类型上均匀运行 .
他们定义了一个简单的"object"模型,并使用有界量化来对所有具有指定集合"attributes"的对象有意义的类型检查函数 .
面向对象语言的更真实的表示将允许作为 recursively-defined types 元素的对象 .
在这种情况下,有限量化不再符合其预期目的 . 很容易找到在具有指定方法集的所有对象上有意义的函数,但这些函数不能在Cardelli-Wegner系统中输入 .
为了在面向对象语言中提供类型化多态函数的基础,我们引入了F-有界量化)
相同硬币的两张脸
编程语言中有两种主要的抽象形式:
参数化和
抽象成员 .
第一种形式是功能语言的典型形式,而第二种形式通常用于面向对象的语言 .
传统上,Java支持值的参数化,以及操作的成员抽象 . 最新的带有泛型的Java 5.0也支持类型的参数化 .
在Scala中包含泛型的论据有两个方面:
首先,对抽象类型的编码并不是直接手工完成的 . 除了简洁性之外,还存在模拟类型参数的抽象类型名称之间意外名称冲突的问题 .
其次,泛型和抽象类型通常在Scala程序中提供不同的角色 .
Generics 通常在需要 type instantiation 时使用,而
abstract types 通常在需要 refer to the abstract type from client code 时使用 .
后者特别出现在两种情况中:
有人可能希望从客户端代码中隐藏类型成员的确切定义,以获得从SML样式模块系统中已知的一种封装 .
或者有人可能想要在子类中共同覆盖类型以获得族多态性 .
在具有有界多态性的系统中,将抽象类型重写为泛型可能需要quadratic expansion of type bounds .
2009年10月更新
Abstract Type Members versus Generic Type Parameters in Scala(Bill Venners)
(强调我的)
例: