Вот как я решил эту проблему:
Черты, реализованные объектом, и черта 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 {