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

ic3*_*3rg 8 reflection scala slick

我有这个基本特征

trait MyBase {
  type M
  type T <: Table[M]
  val query: TableQuery[T]
}
Run Code Online (Sandbox Code Playgroud)

哪里TableQueryscala.slick.lifted.TableQuery

我的子类实例化TableQuery如下:

type M = Account
type T = AccountsTable
val query = TableQuery[T]
Run Code Online (Sandbox Code Playgroud)

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

lazy val query: TableQuery[T] = {
  ...
}
Run Code Online (Sandbox Code Playgroud)

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

Rég*_*les 8

如果我理解正确,你想要的是MyBase通过简单的定义来扩展 M,T但不必TableQuery在每个派生类中显式实例化.

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

如果你绝对需要MyBase成为一个特征(而不是一个类),那么我没有看到任何可行的解决方案.但是,如果你可以把MyBase成类MT成型参数(而不是抽象类型),则存在的至少一种溶液.正如我在另一个相关问题(如何在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()
}
Run Code Online (Sandbox Code Playgroud)

实际效果是,您不再需要知道类型的具体值,T以便能够实例化a TableQuery[T],前提是您具有TableQueryBuilder[T]范围内的隐式实例.换句话说,您可以将需要知道的具体值转移到T 实际知道它的位置.

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

class MyBase[M, T <: Table[M] : TableQueryBuilder] {
  lazy val query: TableQuery[T] = TableQueryBuilder.build[T]
}
Run Code Online (Sandbox Code Playgroud)

然后你可以扩展它而无需explcitly调用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!
Run Code Online (Sandbox Code Playgroud)

这里发生的是在Derived构造函数中,隐式值TableQueryBuilder[Coffees]被隐式传递给MyBase构造函数.

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