【问题标题】:scala: overriding a value of generic, existential type with a concretised typescala:用具体类型覆盖通用的存在类型的值
【发布时间】:2015-05-17 14:57:51
【问题描述】:

我有一个通用特征 MappingPath,它的类型参数不变:

trait MappingPath[X<:AnyMapping, Y<:AnyMapping]

以及它的工厂接口:

trait Pathfinder[X, Y] {
    def apply(fun :X=>Y) :MappingPath[_<:AnyMapping,_<:AnyMapping]
    def get(fun :X=>Y) :Option[MappingPath[_<:AnyMapping, _<:AnyMapping]]
}

我开始了一个适用于单个映射的框架实现:

class MappingPathfinder[M<:AnyMapping, X, Y] extends Pathfinder[X, Y] {
   override def apply(fun :X=>Y) :MappingPath[M, _<:AnyMapping] = ???
   override def get(fun :X=>Y) :Option[MappingPath[M, _<:AnyMapping]] = ???
}

产生一个编译错误,抱怨MappingPathfinder.apply 没有覆盖任何内容并且没有实现Pathfinder.apply。有趣的是,在apply 的返回类型中将M 替换为_&lt;:AnyMapping 使其可以编译,并且没有对类似的get 方法提出任何投诉。

发生了什么事?我使用 scala 2.11.5。

编辑: 我能够通过添加显式存在注释来规避我的问题:

//Pathfinder
def apply(fun :X=>Y) :MappingPath[A, B] forSome { type A<:AnyMapping; type B<:AnyMapping }

//MappingPathfinder
def apply(fun :X=>Y) :MappingPath[A, B] forSome { type A>:M<:M; type B<:AnyMapping }

它似乎工作,即 我能做到:

(p :MappingPath[_<:AnyMapping, M]) ++ mappingPathfinder(f),

其中++ 需要以与this 结尾完全相同的类型开头的路径。它看起来有点傻,但肯定令人困惑。

【问题讨论】:

  • Afaik,def apply(): MappingPath[_&lt;: AnyMapping, _ &lt;: AnyMapping]def apply[A &lt;: AnyMapping, B &lt;: AnyMapping](): MappingPath[A, B] 相同。这样看,我们可以看到签名def apply[B &lt;: AnyMapping](): MappingPath[M, B] 不匹配。但是,那些存在类型真的让我很困惑,我无法弄清楚为什么 get 会编译。如果您只是在寻找解决方案,我会说不要使用存在类型,而是使用 M &lt;: AnyMapping 参数化 PathFinder
  • 真的等价吗?在扩展形式中,调用者可以指定结果特化,而我期望(或至少想要)返回一个真正的 ..[T] forSome { type T}。当然可以在父类型中有一个 Seq[_] 的成员声明并将其覆盖为 Seq[T],其中 T 是覆盖类类型参数,这就是我想要的。

标签: scala generics existential-type


【解决方案1】:

不是答案,但您的用例可以简化为:

trait Higher[U]

trait Super {
  def foo: Higher[_]
}

trait Sub[M] {
  override def foo: Higher[M]  // error: method foo overrides nothing
}

我会使用类型成员,而不是存在类型:

trait Super {
  type U
  def foo: Higher[U]
}

trait Sub[M] {
  type U = M
}

我认为不同的是,在存在类型的情况下,你只指定返回的类型参数有一些上限,但不一定总是同一类型;而在我的第二个示例中,type U 意味着这最终将是一种特定类型,而您只能细化一个特定类型。您可以使上限更精确:

trait Upper

trait A {
  type U <: Upper  
}

trait Specific extends Upper

trait B extends A {
  type U <: Specific   // type is "overridden"
}

如果可能的话,我会避免存在类型,而您的情况似乎非常适合这种避免。大多数时候,只有 Java 互操作才需要存在类型。

【讨论】:

  • 这个解决方案的问题是,当在 sub 中我通过一个等于类型参数的成员类型覆盖上限时,每当我直接使用 sub 时,所有实例的上限都会不同MappingPathfinder[M] 如果由于路径相关类型的工作方式而在使用时该类型是抽象的,则使用相同类型(甚至相同实例的不同引用)参数化。在这种特殊情况下,这对我来说可能不是问题,但总的来说,由于这个原因,使用抽象类型成员是一件非常痛苦的事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-26
  • 1970-01-01
  • 2017-01-11
相关资源
最近更新 更多