Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Generics Scala:抽象类型与泛型_Generics_Scala_Abstract Type - Fatal编程技术网

Generics Scala:抽象类型与泛型

Generics Scala:抽象类型与泛型,generics,scala,abstract-type,Generics,Scala,Abstract Type,我在看书。什么时候使用抽象类型更好 比如说, abstract class Buffer { type T val element: T } abstract class Buffer[T] { val element: T } 比如说泛型 abstract class Buffer { type T val element: T } abstract class Buffer[T] { val element: T } 当我读到有关Scala的文章时,我也有同样

我在看书。什么时候使用抽象类型更好

比如说,

abstract class Buffer {
  type T
  val element: T
}
abstract class Buffer[T] {
  val element: T
}
比如说泛型

abstract class Buffer {
  type T
  val element: T
}
abstract class Buffer[T] {
  val element: T
}

当我读到有关Scala的文章时,我也有同样的问题

使用泛型的优点是您正在创建一个类型族。没有人需要子类化
Buffer
——他们可以使用
Buffer[Any]
Buffer[String]

如果您使用抽象类型,那么人们将被迫创建一个子类。人们需要像
AnyBuffer
StringBuffer
等类


你需要决定哪一个更适合你的特殊需要。

你在这个问题上有很好的观点:


与马丁·奥德斯基的对话,第三部分
比尔·维纳斯和弗兰克·索默斯(2009年5月18日)

更新(2009年10月):Bill Venners的这篇新文章实际上说明了以下内容:
(见最后的总结)


(以下是2009年5月第一次采访的相关摘录,重点)

一般原则 抽象的概念一直有两种:

  • 参数化和
  • 抽象成员
在Java中,两者都有,但这取决于您对什么进行抽象。
在Java中,有抽象方法,但不能将方法作为参数传递。
您没有抽象字段,但可以将值作为参数传递。
同样,您没有抽象类型成员,但可以指定类型作为参数。
所以在Java中,您也有这三个方面,但是对于什么样的事物可以使用什么样的抽象原则是有区别的。你可以说这种区别是相当武断的

斯卡拉路 我们决定对所有三种类型的成员采用相同的构造原则 因此,您可以有抽象字段和值参数。
您可以将方法(或“函数”)作为参数传递,也可以对其进行抽象。
您可以将类型指定为参数,也可以对其进行抽象。
我们从概念上得到的是,我们可以根据另一个对一个进行建模。至少在原则上,我们可以将各种参数化表示为一种面向对象的抽象形式。所以在某种意义上,你可以说Scala是一种更加正交和完整的语言

为什么? 特别是,抽象类型为您提供的是一种很好的处理协方差问题的方法。
一个长期存在的标准问题是动物和食物问题。
难题是要有一个类
动物
,它有一个方法,
,吃一些食物。
问题是,如果我们把动物分为一个亚类,并且有一个类,比如牛,那么它们只吃草,而不吃任意的食物。例如,牛不能吃鱼。
你想说的是,牛有一种只吃草而不吃其他东西的吃法。
实际上,在Java中不能这样做,因为事实证明,可以构造不合理的情况,比如我前面提到的将水果分配给Apple变量的问题

答案是将抽象类型添加到动物类中
你说,我的新动物课有一种我不知道的
适宜食物
所以它是一种抽象类型。您没有给出该类型的实现。然后你就有了一个只吃适合的食物的
eat
方法
然后在
奶牛
类中,我会说,好吧,我有一头奶牛,它扩展了类
动物
,对于
奶牛类型,适宜的食物等于草

因此,抽象类型在我不知道的超类中提供了类型的概念,然后我用我知道的东西在子类中填充它

参数化也一样吗? 你确实可以。你可以用动物吃的食物种类来参数化动物类。
但是在实践中,当您对许多不同的事物执行此操作时,会导致参数爆炸,而且通常情况下,在参数范围内
在1998年的ECOOP上,Kim Bruce、Phil Wadler和我发表了一篇论文,其中我们展示了当你增加你不知道的事情的数量时,典型的程序将以二次方式增长
所以有很好的理由不使用参数,而是使用这些抽象成员,因为它们不会给你这个二次放大


在评论中询问:


你是否认为以下是一个公正的总结:

  • 抽象类型用于“has-a”或“uses-a”关系(例如a
    奶牛吃草
  • 其中as泛型通常是“of”关系(例如,
    int列表

我不确定使用抽象类型和泛型之间的关系有多大不同。 不同之处在于:

  • 如何使用,以及
  • 如何管理参数边界

理解马丁在谈到“参数爆炸”时所说的,并且通常,在<强>参数范围< /强>中,当抽象类型使用泛型建模时,它随后的二次增长,可以考虑……写的“<强> < /强>”。马丁·奥德斯基(Martin Odersky)和马蒂亚斯·曾格(Matthias Zenger)在《2005年OOPSLA》(2007年完成)中引用

相关摘录

定义 抽象类型成员提供了一种灵活的方法来抽象具体类型的组件。
抽象类型可以隐藏有关组件内部的信息,类似于它们在签名中的使用。在一个面向对象的框架中,类可以通过继承进行扩展,它们也可以作为一种灵活的参数化方法(通常称为族多态性,参见本文和作者的论文)

(注:Fami
// Type member version
class MySuite extends FixtureSuite3 with MyHandyFixture {
  // ...
}
// Type parameter version
class MySuite extends FixtureSuite[StringBuilder] with StringBuilderFixture {
  // ...
}
// Type member version
class MySuite extends FixtureSuite with StringBuilderFixture {
  type FixtureParam = StringBuilder
  // ...
}
trait AA[B,C]
trait BB[C,A]
trait CC[A,B]
trait AA[B<:BB[C,AA[B,C]],C<:CC[AA[B,C],B]]
trait BB[C<:CC[A,BB[C,A]],A<:AA[BB[C,A],C]]
trait CC[A<:AA[B,CC[A,B]],B<:BB[CC[A,B],A]]
trait AA[+B<:BB[C,AA[B,C]],+C<:CC[AA[B,C],B]]
trait BB[+C<:CC[A,BB[C,A]],+A<:AA[BB[C,A],C]]
trait CC[+A<:AA[B,CC[A,B]],+B<:BB[CC[A,B],A]]
trait AA[+B<:BB[C,AA[B,C]],+C<:CC[AA[B,C],B]] {
  def forth(x:B):C
  def back(x:C):B
}
trait BB[+C<:CC[A,BB[C,A]],+A<:AA[BB[C,A],C]] {
  def forth(x:C):A
  def back(x:A):C
}
trait CC[+A<:AA[B,CC[A,B]],+B<:BB[CC[A,B],A]] {
  def forth(x:A):B
  def back(x:B):A
}
//one trait to rule them all
trait OO[O <: OO[O]] { this : O =>
  type A <: AA[O]
  type B <: BB[O]
  type C <: CC[O]
}
trait AA[O <: OO[O]] { this : O#A =>
  type A = O#A
  type B = O#B
  type C = O#C
  def left(l:B):C
  def right(r:C):B = r.left(this)
  def join(l:B, r:C):A
  def double(l:B, r:C):A = this.join( l.join(r,this), r.join(this,l) )
}
trait BB[O <: OO[O]] { this : O#B =>
  type A = O#A
  type B = O#B
  type C = O#C
  def left(l:C):A
  def right(r:A):C = r.left(this)
  def join(l:C, r:A):B
  def double(l:C, r:A):B = this.join( l.join(r,this), r.join(this,l) )
}
trait CC[O <: OO[O]] { this : O#C =>
  type A = O#A
  type B = O#B
  type C = O#C
  def left(l:A):B
  def right(r:B):A = r.left(this)
  def join(l:A, r:B):C
  def double(l:A, r:B):C = this.join( l.join(r,this), r.join(this,l) )
}
class ReprO extends OO[ReprO] {
  override type A = ReprA
  override type B = ReprB
  override type C = ReprC
}
case class ReprA(data : Int) extends AA[ReprO] {
  override def left(l:B):C = ReprC(data - l.data)
  override def join(l:B, r:C) = ReprA(l.data + r.data)
}
case class ReprB(data : Int) extends BB[ReprO] {
  override def left(l:C):A = ReprA(data - l.data)
  override def join(l:C, r:A):B = ReprB(l.data + r.data)
}
case class ReprC(data : Int) extends CC[ReprO] {
  override def left(l:A):B = ReprB(data - l.data)
  override def join(l:A, r:B):C = ReprC(l.data + r.data)
}
class ListT {
  type T
  ...
}
class List[T] {...}