【问题标题】:Scala Trait with different def based on Type Parameter基于类型参数的具有不同定义的 Scala Trait
【发布时间】:2021-09-13 16:38:10
【问题描述】:

我正在尝试构建一个用任一类型的具体类实例化的通用 DAO:

  1. 常规
  2. Snappy <:>

我目前有一个描述可用方法的 trait DAO,get/create/upsert。在编译时,我想根据我传递的类型参数更改/定义每个方法。这是一些代码:

trait Regular {
    id: int
}

trait Snap extends Regular {
    isSnappy: Boolean = true
}

trait DAO[T<: Regular] {
  def get(id: String)(implicit x: Param,ct: ClassTag[T]): Future[Either[String, T]] 
}

如果特征是常规的:

 def get(id:String)(implicit x: Param,ct: ClassTag[T]): Future[Either[String, T]] = {
   //Handle regular trait flow
 }

如果特征是 Snap <: regular:>

 def get(id:String)(implicit x: Param,ct: ClassTag[T]): Future[Either[String, T]] = {
   //Handle snappy
 }

两个 def 将返回相同的类型 T。

我希望能够在编译时更改 def,因为我知道 DAO 中的 Type 参数。然后在运行时我希望能够实例化该类,但让它根据传递的类型处理 def。

我不确定如何在 scala 中处理这个问题,无论是基于宏的解决方案、重载方法还是为 Dao 定义的某种类型的隐式。任何方向将不胜感激!我不确定这是否是一个独特的问题,但我已经尽可能多地阅读了有关基于类型参数的编译时间定义,同时仍然能够引用相同的特征(在这种情况下是 DAO)

【问题讨论】:

  • 尽管具有函数式编程结构,Scala 还允许我们以面向对象的方式进行编程,这似乎更接近于您已经在做的事情。您不认为这种行为应该封装在您的 Regular / Snap 特征而不是 DAO 中吗?
  • 行为发生在 dao 中,类似于在此处插入,或在此处和此处插入。它不是 Regular 或 Snap 类可以理解或在其自身中定义的东西。

标签: scala generics scala-macros scala-2.12


【解决方案1】:

对于这种情况,您可能需要一个类型类。有很多关于这方面的内容要阅读,但这里有一个基于您的代码精简版的示例。

首先定义一个包含所有需要操作的类:

trait DaoOps[T] {
  def get(id: String): Int
  def create(id: String): T
  def upsert(id: String): Int
}

然后使用每种类型的实现创建DaoOpsimplicit 实例:

trait Regular {
    def id: Int
}

object Regular {
  // DAO implementation for instances of Regular type
  implicit object dao extends DaoOps[Regular] {
    def get(id: String): Int = ???
    def create(id: String): Regular = ???
    def upsert(id: String): Int = ???
  }
}

trait Snap extends Regular {
  def isSnappy: Boolean = true
}

object Snap {
  // DAO implementation for instances of Snap type
  implicit object dao extends DaoOps[Snap] {
    def get(id: String): Int = ???
    def create(id: String): Snap = ???
    def upsert(id: String): Int = ???
  }
}

最后,使用主类中的 typeclass 来选择正在使用的特定类型的实现:

trait DAO[T <: Regular] {
  // Select the ops for the actual type of T
  def get(id: String)(implicit ops: DaoOps[T]): Int =
    ops.get(id)
}

implicit objects 不必在伴生类中,但编译器会在那里查找它们,因此不必显式地将它们带入作用域。

【讨论】:

  • 谢谢,我想我过于关注用宏来处理这个问题,而不是查看类型+隐式。
  • 有没有办法让 DaoOps 返回具体类的具体类型?像 get[U <: t : classtag string future u class example int extends regular>
  • @athomassi 是的,DaoOps(typeclass)中的类型T是具体类的类型,所以trait中的方法可以返回T,具体实现可以返回RegularSnap 视情况而定。我会调整答案。不确定您是否需要 ClassTag,因为类型是固定的,但需要了解有关整体解决方案的更多信息。
猜你喜欢
  • 2013-07-27
  • 2018-10-24
  • 1970-01-01
  • 1970-01-01
  • 2019-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多