我有这个类型的课:
sealed trait DbValueOps[T <: DbValue] {
type R
def apply(newContent: R): Option[T]
def fromString(newContent: String): Option[T]
def isValidContent(newContent: R): Boolean
}
具有这种类型的类实例:
package object DbOps {
implicit val dbStringOps: DbValueOps[DbString] = new DbValueOps[DbString] {
type R = String
def apply(newContent: String): Option[DbString] =
isValidContent(newContent) match {
case true => Some(new DbString(newContent))
case false => None
}
def fromString(newContent: String): Option[DbString] = this(newContent)
def isValidContent(newContent: String): Boolean = !newContent.isEmpty
}
}
但是当尝试将类型类实例与类似dbStringOps.isValidContent(newContent)
的东西(其中newcontent是字符串)一起使用时,我遇到类型不匹配的情况:
found : newContent.type (with underlying type String)
required: database.DbOps.dbStringOps.R
我可以通过将R从抽象类型成员转换为类型参数来使其工作,但这很丑陋,因为在我编写类型类的实现时已经确定了R。
答案 0 :(得分:7)
在该implicit val
上添加类型注释。
implicit val dbStringOps: DbValueOps[DbString] { type R = String } = ...
或
使用此签名接收隐式参数。
def f()(implicit db: DbValueOps[DbString] { type R = String }) = ...
也可以这样写。
type AUX[A, T] = DbValueOps[A] { type R = T }
def f()(implicit db: AUX[DbString, String]) = ...
答案 1 :(得分:4)
恵砂川答案可以完美地解决您的问题,但是除非您真的希望将设计的中心放在DbValues上,否则我建议将隐式放在包装的值上(在这种情况下为String),因为您不需要提供R与String的统一。
trait DbValue[T]
case class DbString(s:String) extends DbValue[String]
sealed trait DbOps[R]{
type T <: DbValue[R]
def apply(newContent: R): Option[T]
def fromString(newContent: String): Option[T]
def isValidContent(newContent: R): Boolean
}
object DbOps {
val dbStringOps: DbOps[String] = new DbOps[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] = this(newContent)
def isValidContent(newContent: String): Boolean = !newContent.isEmpty
}
val newContent = "hello"
dbStringOps.isValidContent(newContent)
}
将类型参数添加到DbValue可能会更加冗长,但是它阻止您定义诸如DbValueOps[DbString] { type R = Int }
之类的东西,而这些东西可能并不是您想要的。