Scala 反射实例化 scala.slick.lifted.TableQuery [英] Scala reflection to instantiate scala.slick.lifted.TableQuery

查看:29
本文介绍了Scala 反射实例化 scala.slick.lifted.TableQuery的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个基本特征

trait MyBase {M型类型 T <:表[M]val 查询:TableQuery[T]}

其中 TableQueryscala.slick.lifted.TableQuery

我的子类像这样实例化TableQuery:

type M = 账户类型 T = AccountsTableval 查询 = TableQuery[T]

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

懒惰的val查询:TableQuery[T] = {...}

我一直在玩反射,但运气不佳.

解决方案

如果我理解正确,你想要的是能够扩展MyBase 通过简单地定义 MT 而不必在每个派生类中显式实例化 TableQuery.>

使用反射并不是一种真正的选择,因为通常您使用 TableQuery.apply为此(如 val query = TableQuery[MyTable]),这是通过宏实现的,所以你有一个运行时与编译时"的问题.

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

//注意:使用 Scala 2.11.0 & 测试光滑 3.0.0导入 scala.reflect.macros.Context导入 scala.language.experimental.macros对象 TableQueryBuilderMacro {def createBuilderImpl[T<:AbstractTable[_]:c.WeakTypeTag](c: Context) = {导入 c.universe._val T = weakTypeOf[T]q"""new TableQueryBuilder[$T]{def apply(): TableQuery[$T] = {表查询[$T]}}"""}}trait TableQueryBuilder[T<:AbstractTable[_]] {def apply(): TableQuery[T]}对象 TableQueryBuilder {隐式 def builderForTable[T<:AbstractTable[_]]: TableQueryBuilder[T] = 宏 TableQueryBuilderMacro.createBuilderImpl[T]def build[T<:AbstractTable[_]:TableQueryBuilder](): TableQuery[T] = 隐式[TableQueryBuilder[T]].apply()}

最终效果是您不再需要知道 T 类型的具体值,以便能够实例化 TableQuery[T],前提是您在范围内有一个 TableQueryBuilder[T] 的隐式实例.也就是说,你可以转移需要知道T的具体值直到您真正了解为止.

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

class MyBase[M, T <: Table[M] : TableQueryBuilder] {惰性查询: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 * =(名称,价格)}class Derived extends MyBase[(String, Double), Coffees]//就是这样!

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

如果 MyBase 是一个 trait,你不能应用这个模式的原因很普通:trait 构造函数不能有参数,更不用说隐式参数了,所以没有隐式方法传递 TableQueryBuilder 实例.

I have this base trait

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

Where TableQuery is scala.slick.lifted.TableQuery

My subclasses instantiate TableQuery like so:

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

I'd like to instantiate the TableQuery in the base trait, possibly by using a lazy val, i.e.

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

I've been playing around with reflection, but haven't had much luck.

解决方案

If I understand correctly, what you want is to be able to extend MyBase by simply defining M and T but without having to explicitly instantiate the TableQuery in each derived class.

Using reflection is not really an option because normally you use TableQuery.apply for that (as in val query = TableQuery[MyTable]), and this is implemented through a macro, so you've got a "runtime vs compile-time" issue.

If you absolutely need MyBase to be a trait (as opposed to a class), then I don't see any viable solution. However if you can turn MyBase into a class and turn M and T into type parameters (instead of abstract types), then there is at least one solution. As I hinted in another related question (How to define generic type in Scala?), you can define a type class (say TableQueryBuilder) to capture the call to TableQuery.apply (at the point where the concrete type is known) along with an implicit macro (say TableQueryBuilder.builderForTable) to provide an instance of this type class. You can then define a method (say TableQueryBuilder.build) to actually instantiate the TableQuery, which will just delegate to job to the type class.

// 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()
}

The net effect is that you don't need anymore to know the concrete value of the type T in order to be able to instantiate a TableQuery[T], provided that you have an implicit instance of TableQueryBuilder[T] in scope. In other words, you can shift the need to know the concrete value of T up to the point where you actually know it.

MyBase (now a class) can then be implemented like this:

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

And you can then extend it without the need to explcitly call 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!

What happens here is that in Derived's constructor, an implicit value for TableQueryBuilder[Coffees] is implicitly passed to MyBase's constructor.

The reason why you cannot apply this pattern if MyBase were a trait is pretty mundane: trait constructors cannot have parameters, let alone implicit parameters, so there would be no implicit way to pass the TableQueryBuilder instance.

这篇关于Scala 反射实例化 scala.slick.lifted.TableQuery的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆