2010-07-03 167 views
19

在什么情况下抽象类型应优先于类型参数?抽象类型与类型参数

+1

是不是已经在http://stackoverflow.com/questions/1154571/scala-abstract-types-vs-generics/1154727#1154727? – VonC 2010-07-03 08:55:06

+1

@VonC:我看到了答案,但并不满意。 – 2010-07-03 09:11:52

+0

我试图在下面进行说明,然后从Jesse EICHAR发布最新博客文章。 – VonC 2010-07-03 09:16:44

回答

16

要添加到我的previous answer on Abstract type vs. parameters,你也JESSE EICHAR's recent blog post(2010年5月3日)强调的一些主要差异:

trait C1[A] { 
    def get : A 
    def doit(a:A):A 
} 
trait C2 { 
    type A 
    def get : A 
    def doit(a:A):A 
} 

C2情况下,参数是“被埋葬”(作为内部抽象类型)。
(除作为返璞词所说的那样,它实际上并没有埋葬,见下文)

而使用泛型类型,参数明确提到,帮助其他表情就知道他们应该使用何种类型的


所以(C1:参数):

//compiles 
def p(c:C1[Int]) = c.doit(c.get) 

它编译,但你暴露明确要使用 'A' 类型。

和(C2:抽象类型):

// doesn't compile 
def p2(c:C2) = c.doit(c.get) 
<console>:6: error: illegal dependent method type 
     def p2(c:C2) = c.doit(c.get) 
      ^

它不会编译,因为“A”永远不会在P2定义中提及,所以doit不知道在编译类型什么是应该返回。


当使用抽象类型希望避免任何“式泄漏”的界面(即想暴露什么“A”实际上是),你可以指定一个非常通用的类型为P2返回:

// compiles because the internals of C2 does not leak out 
def p(c:C2):Unit = c.doit(c.get) 

或者你也可以 “修复”,直接在doit功能类型:
def doit(a:A):Int,而不是def doit(a:A):A,这意味着:
def p2(c:C2) = c.doit(c.get)将编译(即使P2没有提及任何返回类型)


最后(retronym的评论),你可以明确地炼C2抽象参数指定A

scala> def p2(c:C2 { type A = Int }): Int = c.doit(c.get) 
p2: (c: C2{type A = Int})Int 

或者通过添加一个类型参数(并用它来改进C2抽象类型!)

scala> def p2[X](c:C2 { type A = X }): X = c.doit(c.get) 
p2: [X](c: C2{type A = X})X 

如此抽象建议:

  • 当你想隐藏客户端代码类型成员的确切定义,使用抽象类型像C2(但要谨慎使用功能的定义C2
  • 当您想在的子类中共同覆盖类型,使用抽象类(有限制类型抽象)
  • 当你想通过特质这些C2类型的定义混合,使用抽象类(你会不会有“A”对付混合C2时类:你只混合C2

对于剩下的,在简单类型实例是需要,使用的参数。
(如果你知道,没有扩展将是必要的,但你仍然必须处理几种类型:那是什么参数类型是)


retronym补充说:

主要区别

  • 方差C2只能是不变在A
  • 该类型的成员可在亚型(而类型参数必须被重新声明并传递到超类型)

(如illustrating here

trait T1 { 
    type t 
    val v: t 
} 
trait T2 extends T1 { 
    type t <: SomeType1 
} 
trait T3 extends T2 { 
    type t <: SomeType2 // where SomeType2 <: SomeType1 
} 
class C extends T3 { 
    type t = Concrete // where Concrete <: SomeType2 
    val v = new Concrete(...) 
} 

)被选择性地重写的方式

+6

这不是真正的埋藏:'def p2(c:C2 {type A = Int}):Int = c.doit(c.get)'。或者:'def p2 [X](c:C2 {type A = X}):X = c.doit(c.get)'。主要区别是方差('C2'只能在'A'中不变),并且类型成员可以在子类型中有选择地覆盖,而类型参数必须重新声明并传递给超类型。 – retronym 2010-07-03 16:01:37

+1

@retronym:*“主要区别是方差......以及类型成员可以被选择性覆盖的方式”*这是否意味着与其中一个可能相关的所有事情都可能与其他方式有关?除了你提到的两个方面。唯一的区别是语法? – Lii 2015-12-22 05:07:23