如何指定在类型参数中传递的类具有某个伴侣对象?

时间:2019-04-20 02:38:08

标签: scala generics polymorphism traits companion-object

我有两个特征:

sealed trait DbValue {
  type R
  type T <: DbValue
  def content(): R
  def copy(newContent: R = content): Option[T]
  def toString(): String
}

sealed trait DbValueOps {
  type R
  type T <: DbValue
  def apply(newContent: R): Option[T]
  def fromString(newContent: String): Option[T]
  def isValidContent(newContent: R): Boolean
}

并希望创建一个方法/类,该方法/类采用实现两种特征的对象,但是DbValue由该类实现,而DbValueOps由伴随对象实现。

示例:

case class Column[T <: DbValue with DbValueOps] (val name: String, val cells: Vector[Option[T]] = Vector(), val blank_allowed: Boolean = true) {}

我现在想创建一个对这样实现的类型通用的列:

case class DbString private (val content: String) extends DbValue {
  type R = String
  type T = DbString
  def copy(newContent: String = content): Option[DbString] = DbString(newContent)
  override def toString(): String = content
}

object DbString extends DbValueOps {
  type R = String
  type T = DbString
  def apply(newContent: String): Option[DbString] =
    isValidContent(newContent) match {
      case true => Some(new DbString(newContent))
      case false => None
    }
  def fromString(newContent: String): Option[DbString] = DbString(newContent)
  def isValidContent(newContent: String): Boolean = !newContent.isEmpty
}

但是,当我尝试使用Column[DbString]("name")作这样的专栏时 我收到错误消息:type arguments [database.DbString] do not conform to method apply's type parameter bounds [T <: database.DbValue with database.DbValueOps]

有没有很好的方法可以做到这一点?还是应该更改设计?

2 个答案:

答案 0 :(得分:2)

从OOP的角度来看,类X及其伴随对象X完全不相关(除非它们的成员“彼此可见”,即使它们是私有的)。类X的类型为X,对象X的类型为X.type,并且这些类型也不相关。

尝试

case class Column[T <: DbValue, U <: DbValueOps](...

Column[DbString, DbString.type]("name")

答案 1 :(得分:0)

这就是我最终解决它的方式:

特征由一个对象实现,而ops特征由我认为称为类型类实例的实例化。

sealed trait DbValue[R, T <: DbValue[R, T]] {
  def content(): R
  def copy(newContent: R = content): Option[T]
  def toString(): String
}

sealed trait DbValueOps[R, T <: DbValue[R, T]] {
  def apply(newContent: R): Option[T]
  def fromString(newContent: String): Option[T]
  def isValidContent(newContent: R): Boolean
  def fromDbValue[U, V <: DbValue[U, V]](dbValue: V): Option[T] = fromString(dbValue.toString())
}

,然后我的列仅将类型类实例作为参数(在这种情况下为隐式实例,但这在这种情况下可能不是一件好事)。类型系统强制将typeclass对象与DbValue对象相关联。

case class Column[R, T <: DbValue[R, T]] private (
  val name: String,
  val cells: Vector[Option[T]] = Vector(),
  val blankAllowed: Boolean = true,
  val defaultValue: Option[T] = None,
  )(implicit ops: DbValueOps[R, T]) extends ColumnStringOps {