好吧,也许我正在以错误的方式解决整个事情,但我需要帮助,我找不到解决方案。
我正在编写一个Redis客户端实现,供我自己在Scala中使用。
所以我希望有一个名为RedisListClient[T]
的类,它将在内部处理所有事情,我只需要一个List[T]
来处理。
现在我有一个工厂以下列方式创建这样的客户端:
def createRedisListClient[T <: ByteStringFormattable](name: String): RedisListClient[T] = {
new RedisListClient[T](name)
}
通用类型必须包含特征ByteStringFormattable
。这是必需的,因为redis客户端必须在内部将对象序列化为ByteString,并且必须能够向后执行此操作。
所需的特质就像
一样简单trait ByteStringFormattable {
type T
val formatter: ByteStringFormatter[T]
}
所以我把它全部用于自定义类。让我们说一个ScoreModel,可以保存分数。
class ScoreModel(userId: String, score: Long, scoreText: Option[String] = None) extends ByteStringFormattable {
override type T = ScoreModel
override val formatter: ByteStringFormatter[T] = new ByteStringFormatter[T] {
override def serialize(data: T): ByteString = ByteString(s"$userId#$score#$scoreText")
override def deserialize(bs: ByteString): T = {
val split = bs.utf8String.split("#", 2)
new ScoreModel(split(0), split(1).toLong, split.lift(2).map(s => Some(s)).getOrElse(None))
}
}
}
现在对我来说真正重要的问题是,我如何才能使用createRedisListClient[String]("myName")
?
我的意思是String
没有实现特征ByteStringFormattable
。
我尝试使用这样的隐式转换:
implicit def fromString(s: String): SerializableString = new SerializableString(s)
implicit def toString(sws: SerializableString): String = sws.get
implicit class SerializableString(string: String) extends ByteStringFormattable {
override type T = String
val get = string
override val formatter: ByteStringFormatter[T] = new ByteStringFormatter[String] {
override def serialize(data: String): ByteString = ByteString(data.getBytes("UTF8"))
override def deserialize(bs: ByteString): String = bs.utf8String
}
}
但这没有用。 Scala编译器看到该函数调用的String类型,检查它是否使用了trait,然后失败。
那么,我是否有可能使用基本类型的函数调用,例如String
,Int
和Co以及一些隐式的东西,或者我是否真的必须构建像{一样的Wrapper类上面的示例中是{1}}?
答案 0 :(得分:2)
我没有时间为你编写完整的代码,因为你拥有所有这些Redis依赖项,所以复制它很繁琐,但让我解释一下。
你的方法
def createRedisListClient[T <: ByteStringFormattable](name: String)
只能接受扩展ByteStringFormattable
的内容。如果你希望它能够接受类型T的隐式转换T - > ByteStringFormattable在范围内可用,您必须这样说:
def createRedisListClient[T](name: String)(implicit conv: T => ByteStringFormattable)
您也可以使用view bounds and context bounds之类的语法糖(视图边界已被弃用,因此我会避免使用它们)。
更一般地说,问题的标准解决方案是type classes。我还要指出你的blog post on this topic。我写这篇文章是因为我觉得我们需要一篇文章将所有这些东西粘合在一起,从你遇到的问题到隐式转换,视图/上下文边界和类型类。如果您有15-20分钟的时间,我认为这对您有所帮助。
答案 1 :(得分:1)
从您所使用的代码到工作代码的最短更改是使用视图绑定而不是您现在拥有的绑定。视图绑定会声明存在从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]
}
重新定义为:
com.google.protobuf
现在,我写的这篇文章是用视图绑定编译你想要的东西。但请继续阅读,因为在Scala 2.11中不推荐使用视图边界,所以我稍后会显示更好的,不推荐的方式(隐式参数)。
请注意,我必须调整你对ByteString方法的调用才能在这里进行编译,因为我没有redis的ByteString,并且必须使用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")
}
}
中的那个。
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")
}
}
编译,但正如我所说它使用已弃用的视图边界,现代scala替换为隐式参数。这是使用隐式参数执行所需操作的方法:
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")
}
}
。所以让我们再一次重做它,但是使用隐式格式化程序对象,根本没有格式化特征:
{{1}}
答案 2 :(得分:0)
你真的想打电话给你这样的方法:
createRedisListClient[String]("myName")
即。对于你来说,type参数是String很重要吗?正如在另一个答案中提到的,由于你的类型约束,这不起作用,你必须将其更改为上下文绑定。
但这真的是你想要的,或者你真的不在乎类型参数是什么,只是想将String转换为适当的隐含的东西?
如果您这样调用它,则可以应用隐式转换:
createRedisListClient("myName")
您必须删除隐式转换方法。这些方法是为隐式类生成的。 (实际上,生成这些方法是声明类隐式的唯一方法。)