避免Scala中冗余的泛型参数



这是将Java问题直接移植到scala的例子

我们有一堆接受泛型参数的特征,如下所示:

 trait Ident { }
 trait Container[I <: Ident] {
   def foo(id: I): String
 }
 trait Entity[C <: Container[I], I <: Ident] {
   def container: C
   def foo(id: I) = container.foo(id)
 }

这可以工作,但它有点笨拙,因为我们必须在定义Entity的子类时提供标识的类型和容器的类型。而实际上,容器的类型本身就已经足够了:

class MyIdent extends Ident { }
class MyContainer extends Container[MyIdent] { } 
class MyEntity extends Entity[MyContainer,MyIdent] { }
//                                        ^^^^^^^ shouldn't really be necessary

使用存在类型避免了Entity需要接受两个形参…当然,你以后不能再引用它了。

trait Entity[C <: Container[I] forSome { type I <: Ident }] {
  def container: C
  def foo(id: I) = container.foo(id)
//           ^^^ complains it has no idea what 'I' is here
}

同样,将thing转换为使用成员类型也不起作用…

trait Ident { }
trait Container {
  type I <: Ident
  def foo(id: I): String
}
trait Entity {
  type C <: Container
  def container: C
  def foo(id: C#I) = container.foo(id)
//                                 ^^ type mismatch
}
有谁知道在Scala中是否有一个优雅的解决方案?

更新 给出这个答案我不确定这是否应该被认为是一个bug

你已经击中SI-4377;如果你提供明确的类型归属,你会得到一个错误,我猜这只是暴露了类型投影是使用存在性实现的:

trait Ident { }
trait Container {
  type I <: Ident
  def foo(id: I): String
}
trait Entity {
  type C <: Container
  def container: C
  def foo(id: C#I): String = (container: C).foo(id: C#I)
  // you will get something like: type mismatch;
  // [error]  found   : Entity.this.C#I
  // [error]  required: _3.I where val _3: Entity.this.C
  // as I said above, see https://issues.scala-lang.org/browse/SI-4377
}

可以毫不夸张地说,这(bug?)使使用类型成员的泛型编程成为一场噩梦。

是一个hack,它包括将转换为手工制作的自引用类型别名:

case object Container {
  type is[C <: Container] = C with Container {
    type I = C#I
    // same for all other type members, if any
  }
  def is[C <: Container](c: C): is[C] = c.asInstanceOf[is[C]]
}

现在使用它和Entity编译:

trait Entity {
  type C <: Container
  def container: C
  def foo(id: C#I): String = Container.is(container).foo(id)
  // compiles!
}

这当然是危险的,根据经验,只有当C及其所有类型成员在使用时被绑定到非抽象类型时,它才是安全的;请注意,情况并非总是如此,因为Scala允许您保留"未定义"类型成员:

case object funnyContainer extends Container {
  // I'm forced to implement `foo`, but *not* the `C` type member
  def foo(id: I): String = "hi scalac!"
}

相关内容

  • 没有找到相关文章

最新更新