scala 将类及其伴随对象作为类型参数的泛型特征

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10512009/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 04:09:57  来源:igfitidea点击:

generic trait taking a class and it's companion object as a type parameter

scalagenerics

提问by gimo4000

So I want to have a generic trait take as a type parameter a class with a companion object that inherits from a specific base class and refer to both the companion object and the class itself. So,

所以我希望有一个泛型特征将一个类作为类型参数,该类带有一个从特定基类继承的伴生对象,并同时引用伴生对象和类本身。所以,

abstract class BaseModel[T] {
  def all: Seq[T]
}

case class Customer(email: String, password: String)

object Customer extends BaseModel[Customer]

// This trait is my issue
trait BaseCrud[T] { 
  def table[T](f: T => String): String = {
    T.all.map(f _).mkString("")
  }
}

object Controller with BaseCrud {
  def foo = table(_.email)
}

I had some solutions to that trait that were closer but I distilled it down so you can see what I am trying to do.

我有一些更接近该特征的解决方案,但我将其提炼出来,以便您可以看到我正在尝试做什么。

Thanks

谢谢

UPDATE

更新

So I went with solution from Frank below, but I did manage to solve my initial puzzle. Though, in this case the solution was a bit ugly I'll include it here for completeness sake.

所以我采用了下面弗兰克的解决方案,但我确实设法解决了我最初的难题。不过,在这种情况下,解决方案有点难看,为了完整起见,我将其包含在此处。

abstract class BaseModel[T] {
  def all: Seq[T] = Seq()
}

case class Customer(email: String, password: String)

object Customer extends BaseModel[Customer]

trait BaseCrud[T, U <: BaseModel[T]] { 
  def table(f: T => String)(implicit obj: U): String = {
    obj.all.map(f(_)).mkString("")
  }
}

object Controller extends BaseCrud[Customer, Customer.type] {
  implicit val model = Customer
  def foo = table(_.email)
}

So the type parameters changed to BaseCrud and the implicit was added to BaseCrud.table and implemented in Controller.model. I also fixed all my typos. I found it interesting Customer.type seems to be the type of the companion object.

所以类型参数改为BaseCrud,隐式被添加到BaseCrud.table并在Controller.model中实现。我还修正了我所有的错别字。我觉得很有趣 Customer.type 似乎是伴随对象的类型。

回答by Frank

There's a bunch of problems in your code.. let's tackle it one after the other:

你的代码中有一堆问题..让我们一个接一个地解决它:

  • def table[T](...note that this Toverwrites the original type parameter for the scope of the method. Not what you want really, so just omit it and make this def table(...
  • object Controller with BaseCrud {contains two more mistakes:
    1. it has to be extendsnot with. The latter is only used after you already extended from some base-class/trait.
    2. BaseCrudrequires a type parameter that you have to specify here, so something like BaseCrud[Customer]
  • and finally, to answer your actual question: There is a huge difference between a typeparameter Tand a companion object. They are inherently different things, so you cannot access the companion object via T.something. Instead you need to provide the companion object in your trait in some other way, for example as an abstract field.
  • def table[T](...请注意,这会T覆盖方法范围的原始类型参数。不是你真正想要的,所以省略它并做这个def table(...
  • object Controller with BaseCrud {还有两个错误:
    1. 它必须extends不是with。后者仅在您已经从某些基类/特征扩展后使用。
    2. BaseCrud需要一个您必须在此处指定的类型参数,因此类似于 BaseCrud[Customer]
  • 最后,回答您的实际问题:类型参数T和伴随对象之间存在巨大差异。它们本质上是不同的东西,所以你不能通过T.something. 相反,您需要以其他方式在特征中提供伴随对象,例如作为抽象字段。

Here's a version of what I believe you want to do:

这是我相信您想要做的一个版本:

abstract class BaseModel[T] {
  def all: Seq[T]
}

case class Customer(email: String, password: String)

object Customer extends BaseModel[Customer] {
  def all = List[Customer]() // have to define this
}

trait BaseCrud[T] {
  val companion : BaseModel[T]
  def table(f: T => String): String = {
    companion.all.map(f).mkString("")
  }
}

object Controller extends BaseCrud[Customer] {
  val companion = Customer
  def foo = table(_.email)
}

回答by 4e6

I think, you can use scala selftype in BaseCrudtrait to specify a class it should be mixed in. See linked questionfor details.

我认为,您可以selfBaseCrudtrait 中使用 Scala类型来指定它应该混合的类。有关详细信息,请参阅链接问题

trait BaseCrud[T] {
  self: BaseModel[T] =>
  def table(f: T => String): String =
    all.map(f).mkString("")
}

object Controller extends BaseModel[Customer] with BaseCrud[Customer]{
  def foo = table(_.email)
}