从你所拥有的代码到工作代码的最短变化是使用 view bound 而不是你现在拥有的 bound。视图绑定会断言存在从T 到ByteStringFormattable 的某种隐式转换,这涵盖了T 是子类的情况,但也涵盖了String 的情况。
但是,在我们这样做之前,我认为需要进行一些重新安排。具体来说,我认为您对ByteStringFormattable 的定义有问题,因为被转换的类型与ByteStringFormattable 之间没有任何联系。照原样,现在你可以写这个,它会编译,即使它是非常错误的:
class ScoreModel(userId: String, score: Long, scoreText: Option[String] = None) extends ByteStringFormattable {
override type T = String
override val formatter: ByteStringFormatter[T] = new ByteStringFormatter[T] {
override def serialize(data: String): ByteString = ByteString(data.getBytes("UTF8"))
override def deserialize(bs: ByteString): String = bs.utf8String
}
}
也就是说,现在您没有类型保证返回的 ByteStringFormatter 将是正确类的格式化程序。
因此,我将ByteStringFormattable 重新定义为:
trait ByteStringFormattable[T] {
val formatter: ByteStringFormatter[T]
}
那么,这里是我写的一些东西,它编译了你想要的东西,并带有一个视图绑定。但是请继续阅读,因为视图边界在 Scala 2.11 中已被弃用,所以我稍后会展示更好的、非弃用的方式(隐式参数)。
请注意,我必须调整您对 ByteString 上的方法的调用才能在此处编译它,因为我没有 redis 的 ByteString,必须使用来自 com.google.protobuf 的那个。
class RedisListClient[T <% ByteStringFormattable[T]](val name: String) {
}
trait ByteStringFormatter[T] {
def serialize(data: T): ByteString
def deserialize(bs: ByteString): T
}
trait ByteStringFormattable[T] {
val formatter: ByteStringFormatter[T]
}
class ScoreModel(userId: String, score: Long, scoreText: Option[String] = None) extends ByteStringFormattable[ScoreModel] {
override val formatter: ByteStringFormatter[ScoreModel] = new ByteStringFormatter[ScoreModel] {
override def serialize(data: ScoreModel): ByteString = ByteString.copyFromUtf8(s"$userId#$score#$scoreText")
override def deserialize(bs: ByteString): ScoreModel = {
val split = bs.toStringUtf8.split("#", 2)
new ScoreModel(split(0), split(1).toLong, split.lift(2).map(s => Some(s)).getOrElse(None))
}
}
}
object RedisListClient {
implicit class SerializableString(string: String) extends ByteStringFormattable[String] {
val get = string
override val formatter: ByteStringFormatter[String] = new ByteStringFormatter[String] {
override def serialize(data: String): ByteString = ByteString.copyFrom(data.getBytes("UTF8"))
override def deserialize(bs: ByteString): String = bs.toStringUtf8
}
}
def createRedisListClient[T <% ByteStringFormattable[T]](name: String): RedisListClient[T] = {
new RedisListClient[T](name)
}
def testIt() = {
val x = createRedisListClient[ScoreModel]("ClientSM")
val y = createRedisListClient[String]("Wat")
}
}
编译,但正如我所说,它使用不推荐使用的视图边界,在现代 scala 中被隐式参数替换。这是一种使用隐式参数做你想做的事情的方法:
class RedisListClient[T](val name: String, conv: T => ByteStringFormattable[T]) {
}
trait ByteStringFormatter[T] {
def serialize(data: T): ByteString
def deserialize(bs: ByteString): T
}
trait ByteStringFormattable[T] {
val formatter: ByteStringFormatter[T]
}
class ScoreModel(userId: String, score: Long, scoreText: Option[String] = None) extends ByteStringFormattable[ScoreModel] {
override val formatter: ByteStringFormatter[ScoreModel] = new ByteStringFormatter[ScoreModel] {
override def serialize(data: ScoreModel): ByteString = ByteString.copyFromUtf8(s"$userId#$score#$scoreText")
override def deserialize(bs: ByteString): ScoreModel = {
val split = bs.toStringUtf8.split("#", 2)
new ScoreModel(split(0), split(1).toLong, split.lift(2).map(s => Some(s)).getOrElse(None))
}
}
}
object RedisListClient {
implicit class SerializableString(string: String) extends ByteStringFormattable[String] {
val get = string
override val formatter: ByteStringFormatter[String] = new ByteStringFormatter[String] {
override def serialize(data: String): ByteString = ByteString.copyFrom(data.getBytes("UTF8"))
override def deserialize(bs: ByteString): String = bs.toStringUtf8
}
}
def createRedisListClient[T](name: String)(implicit mkFormatter: T => ByteStringFormattable[T]): RedisListClient[T] = {
new RedisListClient[T](name, mkFormatter)
}
def testIt() = {
val x = createRedisListClient[ScoreModel]("ClientSM")
val y = createRedisListClient[String]("Wat")
}
}
这一切都很好,但是如果您已经打算使用隐式参数,您可能想要的是隐式ByteStringFormatter 对象,而不是必须在每个元素上调用formatter。因此,让我们再重做一次,但使用隐式格式化程序对象,并且根本没有可格式化的特征:
class RedisListClient[T](val name: String, formatter: ByteStringFormatter[T]) {
}
trait ByteStringFormatter[T] {
def serialize(data: T): ByteString
def deserialize(bs: ByteString): T
}
class ScoreModel(userId: String, score: Long, scoreText: Option[String] = None) {
def toBs: ByteString = ByteString.copyFromUtf8(s"$userId#$score#$scoreText")
}
object RedisListClientImplicits {
implicit val formatterSM: ByteStringFormatter[ScoreModel] = new ByteStringFormatter[ScoreModel] {
override def serialize(data: ScoreModel): ByteString = data.toBs
override def deserialize(bs: ByteString): ScoreModel = {
val split = bs.toStringUtf8.split("#", 2)
new ScoreModel(split(0), split(1).toLong, split.lift(2).map(s => Some(s)).getOrElse(None))
}
}
implicit val formatterString: ByteStringFormatter[String] = new ByteStringFormatter[String] {
override def serialize(data: String): ByteString = ByteString.copyFrom(data.getBytes("UTF8"))
override def deserialize(bs: ByteString): String = bs.toStringUtf8
}
}
object RedisListClient {
def createRedisListClient[T](name: String)(implicit f: ByteStringFormatter[T]): RedisListClient[T] = {
new RedisListClient[T](name, f)
}
}
object RedisListClientTest {
import RedisListClient._
import RedisListClientImplicits._
def testIt() = {
val x = createRedisListClient[ScoreModel]("ClientSM")
val y = createRedisListClient[String]("Wat")
}
}