Scala反射实例化scala.slick.lifted.TableQuery

时间:2015-05-12 06:05:09

标签: scala reflection slick

我有这个基本特征

trait MyBase {
  type M
  type T <: Table[M]
  val query: TableQuery[T]
}

TableQueryscala.slick.lifted.TableQuery

的位置

我的子类像这样实例化TableQuery

type M = Account
type T = AccountsTable
val query = TableQuery[T]

我想在基本特征中实例化TableQuery,可能使用lazy val,即

lazy val query: TableQuery[T] = {
  ...
}

我一直在玩反思,但运气不好。

1 个答案:

答案 0 :(得分:8)

如果我理解正确,你想要的就是能够扩展 MyBase只需定义MT,但无需在每个派生类中显式实例化TableQuery

使用反射实际上不是一种选择,因为通常你使用TableQuery.apply 为此(如在val query = TableQuery[MyTable]中),这是通过宏实现的, 所以你有一个“运行时与编译时”的问题。

如果你绝对需要MyBase成为一个特征(而不是一个类),那么我没有看到任何可行的解决方案。 但是,如果您可以将MyBase转换为并将MT转换为类型参数(而不是抽象类型),那么至少有一个解决方案。 正如我在另一个相关问题(How to define generic type in Scala?)中暗示的那样,你可以 定义一个类型类(比如TableQueryBuilder)来捕获对TableQuery.apply的调用(在具体类型已知的位置)以及一个隐式宏(比如TableQueryBuilder.builderForTable)来提供 此类型类的实例。然后,您可以定义一个方法(比如TableQueryBuilder.build)来实际实例化TableQuery,它将委托给类型类的作业。

// NOTE: tested with scala 2.11.0 & slick 3.0.0
import scala.reflect.macros.Context
import scala.language.experimental.macros
object TableQueryBuilderMacro {
  def createBuilderImpl[T<:AbstractTable[_]:c.WeakTypeTag](c: Context) = {
    import c.universe._
    val T = weakTypeOf[T]
    q"""new TableQueryBuilder[$T]{
      def apply(): TableQuery[$T] = {
        TableQuery[$T]
      }
    }"""
  }
}
trait TableQueryBuilder[T<:AbstractTable[_]] {
  def apply(): TableQuery[T]
}
object TableQueryBuilder {
  implicit def builderForTable[T<:AbstractTable[_]]: TableQueryBuilder[T] = macro TableQueryBuilderMacro.createBuilderImpl[T]
  def build[T<:AbstractTable[_]:TableQueryBuilder](): TableQuery[T] = implicitly[TableQueryBuilder[T]].apply()
}

净效应是您不再需要知道类型T的具体值,以便能够实例化TableQuery[T], 只要您在范围内有TableQueryBuilder[T]的隐式实例。换句话说,您可以转移需要知道T的具体值 直到你真正了解它。

MyBase(现在是一个类)可以像这样实现:

class MyBase[M, T <: Table[M] : TableQueryBuilder] {
  lazy val query: TableQuery[T] = TableQueryBuilder.build[T]
}

然后您可以扩展它而无需明确地调用TableQuery.apply

class Coffees(tag: Tag) extends Table[(String, Double)](tag, "COFFEES") {
  def name = column[String]("COF_NAME")
  def price = column[Double]("PRICE")
  def * = (name, price)
}

class Derived extends MyBase[(String, Double), Coffees] // That's it!

这里发生的是Derived的构造函数中隐含的TableQueryBuilder[Coffees]隐含值 传递给MyBase的构造函数。

如果MyBase是一个特征,你不能应用这种模式的原因是非常平凡的:特征构造函数不能有参数,更不用说隐式参数了,所以没有隐含的方法 传递TableQueryBuilder实例。

相关问题