使用来自超类的重载构造函数

时间:2014-07-25 14:15:43

标签: scala inheritance constructor

我正在写一个消息解析器。假设我有一个带有两个辅助构造函数的超类Message,一个接受String原始消息,另一个接受Map,其中数据字段以键值对映射。

class Message {
  def this(s: String)
  def this(m: Map[String, String])

  def toRaw = { ... } # call third party lib to return the generated msg
  def map # call third party lib to return the parsed message

  def something1 # something common for all messages which would be overriden in child classes
  def something2 # something common for all messages which would be overriden in child classes
  ...
}

有很好的理由这样做,因为解析/生成的库有点尴尬,并且删除与它连接成一个单独的类的复杂性是有意义的,子类看起来像这样:< / p>

class SomeMessage extends Message {
  def something1 # ...
  def something2 # ...
}

并且想法是在子类中使用重载的构造函数,例如:

val msg = new SomeMessage(rawMessage) # or
val msg = new SomeMessage("fld1" -> ".....", "fld2" -> "....")

# and then be able to call
msg.something1
msg.something2 # ...

然而,辅助构造函数和继承似乎在Scala中的行为方式已经证明这种模式非常具有挑战性,我到目前为止找到的最简单的解决方案是创建一个名为constructMe的方法,它完成了上述案例中的构造函数:

val msg = new SomeMessage
msg.constructMe(rawMessage) # or
msg.constructMe("fld1" -> ".....", "fld2" -> "....")

这似乎很疯狂,需要一个名为constructMe的方法。

所以,问题是:

有没有一种方法来构造代码,以便简单地使用超类中的重载构造函数?例如:

val msg = new SomeMessage(rawMessage) # or
val msg = new SomeMessage("fld1" -> ".....", "fld2" -> "....")

或者我只是以错误的方式解决问题?

1 个答案:

答案 0 :(得分:1)

除非我遗漏了某些内容,否则你正在调用构造函数:

val msg = new SomeMessage(rawMessage)

但是Message类没有参数,你的类应该这样定义:

class Message(val message: String) {
  def this(m: Map[String, String]) = this("some value from mapping")
} 

另请注意,scala中的构造函数必须将主构造函数作为第一个操作调用see this question for more info.

然后扩展Message类的类应该是这样的:

class SomeMessage(val someString: String) extends Message(someString) {
  def this(m: Map[String, String]) = this("this is a SomeMessage")
}

请注意,构造函数需要一个代码块,否则您的代码无法编译,您无法提供def this(someString: String)之类的定义而无需提供实现。

编辑:

说实话,我不明白为什么你要在你的架构中使用Map,你的班级主要指出它包含String,与复杂类型有关构造函数可能导致问题。我们假设你有一些类可以将Map[String, String]作为构造函数参数,你会用它做什么?正如我所说,构造函数必须将自己称为第一条指令,你可能会这样:

class A(someString: String) = {
  def this(map: Map[String, String]) = this(map.toString)
}

就是这样,scala中的限制不允许你做更多的事情,你会想要做一些验证,比如让我们说你想要总是第二次在地图中的元素,这可能会抛出异常,因为用户不会被迫提供具有多个值的地图,除非您开始使用require填充您的课程,否则他甚至不会被迫提供填充的地图秒。

在您的情况下,我可能会将String作为类参数或List[String],您可以在其中调用mkStringtoString

无论如何,如果你对调用map.toString感到满意,你必须将构造函数实现给父类和子类,这是scala构造函数限制之一(在Java中你可以用不同的方式解决问题),我希望有人会证明我错了,但据我所知,没有别的办法可以做到。

作为旁注,我个人觉得这种限制是正确的(大多数时候),因为你强迫你构建你的代码更加严谨并拥有更好的架构,考虑允许人们这样做的事实在构造函数中做任何他们想做的事情(比如在java中)混淆了他们的真正目的,那就是返回一个类的新实例。