Как я могу указать, что класс, переданный в параметре типа, имеет определенный сопутствующий объект?

У меня есть эти две черты:

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]

Есть ли хороший способ сделать это? Или как мне изменить свой дизайн?


person Frederik Baetens    schedule 20.04.2019    source источник


Ответы (2)


Класс X и его сопутствующий объект X совершенно не связаны с точки зрения ООП (за исключением того, что их члены "видят" друг друга, даже если они закрыты). Тип класса X — это X, тип объекта X — это X.type, и эти типы также не связаны между собой.

Пытаться

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

Column[DbString, DbString.type]("name")
person Dmytro Mitin    schedule 20.04.2019
comment
Могу ли я каким-то образом обеспечить, чтобы DbValueOps был сопутствующим объектом для DbValue? Потому что таким образом я мог бы сделать T DbString, а U сопутствующим объектом, связанным, например, с DbInt. Возможно, изменив сигнатуру типа признаков? - person Frederik Baetens; 20.04.2019
comment
@FrederikBaetens stackoverflow.com/questions/9172775/ stackoverflow.com/questions/39260122/ - person Dmytro Mitin; 20.04.2019

Вот как я решил это:

Черты, одна из которых реализована объектом, а черта 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())
}

а затем мой столбец просто принимает экземпляр класса типа в качестве параметра (в данном случае неявный, но, вероятно, в этом сценарии это не очень хорошо). Система типов обеспечивает связь объекта класса типов с объектом 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 {
person Frederik Baetens    schedule 03.05.2019