在scala中使用值类来实现特征方法?

时间:2015-02-12 03:34:30

标签: scala implicit

我有一个定义函数的特性 - 我不想指定它将如何工作直到以后。这个特征与几个案例类混合在一起,如下所示:

trait AnItem
trait DataFormatable {
  def render():String = "" // dummy implementation
}
case class Person(name:String, age:Int) extends DataFormatable with AnItem
case class Building(numFloors:Int) extends DataFormatable with AnItem

好的,现在我想要包含这种渲染行为的特定实现的可包含模块。试着在这里使用价值类:

object JSON {
  implicit class PersonRender( val p:Person ) extends AnyVal {
    def render():String = {
      //render json
    }
  }
  // others
}

object XML {
  implicit class PersonRender( val p:Person ) extends AnyVal {
    def render():String = {
      //render xml
    }
  }
  // others
}

理想的使用方式如下(假设需要JSON输出):

import JSON._
val p:AnItem = Person("John",24)
println(p.render())

一切都很酷 - 但它不起作用。有没有办法让这个可加载实现的东西工作?我接近了吗?

1 个答案:

答案 0 :(得分:0)

DataFormatable特质在这里什么也没做,只能阻止你。你应该摆脱它。由于您希望根据范围中存在的隐含来替换render实现,Person 不能拥有自己的render方法。如果PersonRender首先没有名为Person的方法,编译器将只查找到render的隐式转换。但是因为Personrender继承(或被迫实现)DataFormatable,所以不需要寻找隐式转换。

根据您的修改,如果您有List[AnItem]的集合,则也无法将元素隐式转换为render。虽然每个子类可能都有一个隐式转换,它给出了render,但编译器并不知道它们何时被全部堆积到更抽象类型的列表中。特别是AnItem

等空特征

你怎么能做这个工作?你有两个简单的选择。

其一,如果你想坚持隐式转换,你需要删除DataFormatable作为你的案例类的超类型,这样他们就没有自己的render方法。然后,您可以换出XML._JSON._,转换应该有效。但是,您不会被允许混合收藏。

二,完全放弃隐含,让你的特质看起来像这样:

trait DataFormatable {
    def toXML: String
    def toJSON: String
}

这样,您强制在DataFormatable中混合的每个类都包含序列化信息(这是应该的方式,而不是将它们隐藏在含义中)。现在,当您拥有List[DataFormatable]时,您可以证明所有元素都可以转换为JSON或XML,因此您可以转换混合列表。我认为这总体上要好得多,因为代码应该更直接。你有什么进口不应该真正定义下面的行为。想象一下,由于XML._已导入文件顶部而不是JSON._,因此可能会出现混淆。