【问题标题】:How to define generic type in Scala?如何在 Scala 中定义泛型类型?
【发布时间】:2013-11-20 07:50:38
【问题描述】:

在 Slick 2 中,我们可以像这样映射表:

case class Cooler(id: Option[Int], minTemp: Option[Double], maxTemp: Option[Double])

/**
 * Define table "cooler".
 */
class Coolers(tag: Tag) extends Table[Cooler](tag, "cooler") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
  def minTemp = column[Double]("min_temp", O.Nullable)
  def maxTemp = column[Double]("max_temp", O.Nullable)

  def * = (id.?, minTemp.?, maxTemp.?) <> (Cooler.tupled, Cooler.unapply _)
}

object Coolers {
  val tableQuery = TableQuery[Coolers]
}

因为我有很多表,我想为它们定义通用方法,例如finddeleteupdate,所以我必须在一个超类中定义这些方法,从哪里扩展我的对象( object Coolers extends TableUtils[Coolers, Cooler])。为了定义这些方法,我需要tableQuery 移出我在这个超类中的对象,所以我试了一下:

abstract class TableUtils[T <: Table[A] , A] {

val tableQuery = TableQuery[T]  

}

但我收到tableQuery 定义错误:

class type required but T found

有人知道我做错了什么吗?

【问题讨论】:

    标签: scala slick


    【解决方案1】:

    当您调用TableQuery[T] 时,您实际上是在调用TableQuery.apply,即is actually a macro

    此宏的主体尝试实例化T,但在您的情况下,T 已成为编译器不知道如何实例化的(未知)类型参数。问题类似于尝试编译这个:

    def instantiate[T]: T = new T
    // Does not compile ("class type required but T found")
    

    最终效果是TableQuery.apply 只能用于具体类型。

    可以解决using a type class to capture the call to TableQuery.apply(在具体类型已知的位置)以及隐式宏以提供此类型类的实例。然后你会有类似的东西:

    abstract class TableUtils[T <: Table[A] : TableQueryBuilder, A] {
      val tableQuery = BuildTableQuery[T]
    }
    

    其中TableQueryBuilder 是类型类,BuildTableQueryTableQuery.apply 的替代版本,它将转发到TableQueryBuilder 实例以执行实际实例化。

    我添加了一个实现作为another answer here 的一部分。

    tableQuery 声明为抽象值并在TableUtils 的每个具体派生类中定义它会更容易(如果不太方便):

    abstract class TableUtils[T <: Table[A] , A] {
      val tableQuery: TableQuery[T, T#TableElementType]
      // define here your helper methods operating on `tableQuery`
    }
    object Coolers extends TableUtils[Coolers, Cooler] {
      val tableQuery = TableQuery[Coolers]
    }
    

    【讨论】:

    • 使用最后一种方法,我收到以下错误:wrong number of type arguments for TableQuery, should be 1。实施时有什么特别需要考虑的吗?还是有一个可行的实现?
    • 他们似乎在 2.0.0-M32.0.0 版本之间更改了 TableQuery。尝试用val tableQuery: TableQuery[T]替换val tableQuery: TableQuery[T, T#TableElementType]
    • 我实际上已经实现了类型类 (TableQueryBuilder) 解决方案以获得相关答案。见stackoverflow.com/a/30354736/1632462
    【解决方案2】:

    这是一种解决方案:

    首先,定义它以避免类类型问题..

    class Service[T <: Table[_]](path: String, cons: Tag => T){
    
      lazy val db = Database.forConfig(path)
    
      def query = TableQuery[T](cons)
    }
    

    那么这样使用,Post是Table的子类:

    object Abcd {
    
      object Def extends Service[Post]("mydb", abc) {
    
    
    
        def test = {
    
    
          //db
    
          val q = query.drop(1).take(20)
          val r = db.run(q.result)
    
          println(q.result.statements.head)
          println(r)
    
          r
    
        }
      }
    
      private def abc(tag: Tag) = new Post(tag)
    
    }
    

    此解决方案在 slick 3.x 和 Play slick 1.x 中测试正常,因为 slick 2.0 Query.scala 符合 slick 3.0 Query.scala,这可能也适用于 2。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-21
      • 2013-07-06
      • 2020-09-10
      相关资源
      最近更新 更多