【问题标题】:How do I use a generic type projection in a method parameter?如何在方法参数中使用泛型类型投影?
【发布时间】:2013-12-26 17:46:17
【问题描述】:

我有案例类,并且为每个案例类 T 我定义了一个 Entity[T] 隐式。这些实例定义了一个特定于每个 T 的 Id 类型。然后我定义了一个带有检索方法的抽象 Table[T] 类,该方法通过类型投影获取 T 中定义的 Id 类型的标识符...

但它不起作用,我得到类型不匹配:

scalac generic-type-projection.scala
generic-type-projection.scala:31: error: type mismatch;
 found   : id.type (with underlying type Entity[T]#Id)
 required: _5.Id where val _5: Entity[T]
        val key = implicitly[Entity[T]].keyFromId(id)  // Type mismatch on 'id'.
                                              ^
generic-type-projection.scala:41: error: type mismatch;
 found   : String("dummy")
 required: Entity[A]#Id
    t.retrieve("dummy")
           ^
two errors found

代码如下:

import java.util.UUID

trait Entity[T] {
  type Id
  type Key
  def getId(entity: T): Id
  def keyFromId(id: Id): Key
}

case class A(f1: String, f2: Int)

object AImplicits {
  implicit object AEntity extends Entity[A] {
    type Id = String
    type Key = UUID
    def getId(entity: A) = entity.f1
    def keyFromId(id: String) = UUID.fromString(id)
  }
}

object Main {
  abstract class Table[T: Entity] {
    def store(entity: T): Unit
    def retrieve(id: Entity[T]#Id): Option[T]
  }

  def makeTable[T: Entity]: Table[T] =
    new Table[T] {
      def store(entity: T): Unit = {}
      def retrieve(id: Entity[T]#Id): Option[T] = {
        val key = implicitly[Entity[T]].keyFromId(id)  // Type mismatch on 'id'.
        None
      }
    }

  def main(args: Array[String]) {
    import AImplicits._
    val t = makeTable[A]
    val a = A("dummy", 9)
    t.store(a)
    t.retrieve("dummy")  // Type mismatch on "dummy".
  }
}

非常感谢任何帮助。

【问题讨论】:

  • E 中的 def store(entity: E): Unit 是什么
  • 对不起,这是一个混淆。应该没有Es,只有Ts。我编辑了代码。谢谢。

标签: scala generics types projection


【解决方案1】:

当您展开上下文绑定简写时,类型不匹配会更容易看到。请记住,[T : Entity] 语法只是要求隐式 Entity[T] 在范围内的语法糖。所以你的 makeTable 函数的标题实际上是:

def makeTable[T](implicit entity: Entity[T]): Table[T] =

现在有了作用域中的隐式参数,您在函数中的implicitly 调用将获取该值(实际上,new Table[T] 构造函数获取隐式参数,然后implicitly 从那里获取它),所以@987654328 @函数的扩展形式:

def makeTable[T](implicit entity: Entity[T]): Table[T] =
  new Table[T] {
    def store(entity: T): Unit = {}
    def retrieve(id: Entity[T]#Id): Option[T] = {
      val key = entity.keyFromId(id)  // Type mismatch on 'id'.
      None
    }
  }

请注意,retrieveid 参数的类型声明没有发生转换。本质上,编译器抱怨entity.keyFromId 需要entity.Id 类型的参数,但您的id 参数是抽象类型Entity[T]#Id

这里的问题是您想对Table 类的用户隐藏Entity 和ID,但您想在其中一种方法中公开Id 的类型。假设我有两个Table[T] 实例,但它们使用不同的Entity[T] 实现——如此不同以至于它们的Id 类型实际上是不同的。表实例的用户如何知道将哪种类型传递给retrieve 函数?您可以通过向Table 类添加另一个类型参数来解决此问题。您仍然可以抽象出 id/key 生成逻辑,但这将允许编译器进行类型检查以确保 retrieve 函数获取正确类型的参数。

【讨论】:

  • 你说得对,它是一个依赖于路径的类型问题。我的问题表述得很糟糕,因为我实际上是在寻找一种解决方案来使隐式参数的类型与抽象类型相匹配(在类型投影中)...我终于找到了一种方法,即使用带有抽象 val 的早期初始化程序设置为隐含对象。接受您的回复作为答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-07
  • 1970-01-01
相关资源
最近更新 更多