我相信在Scala中,就像在Java中一样,子类字段在超级构造函数执行后初始化。鉴于此,我正在努力确定如何最好地创建可以在我的子类中初始化但在我的抽象父类的构造函数中验证(或用于验证其他字段)的“抽象字段”。举一个不起作用的简单例子:
abstract class ApiClient(contentType: String) {
val supportedContentTypes: List[String]
if (!(supportedContentTypes contains contentType)) {
throw new RuntimeException("Type " + contentType + " not supported")
}
}
class FacebookClient(contentType: String) extends ApiClient(contentType) {
override val supportedContentTypes = List("text/xml", "application/xml")
}
val api = new FacebookClient("text/xml") // Throws NullPointerException
这个问题在Java中有很多讨论(例如here和here),一般的答案是将“抽象字段”放在父类的构造函数中。这个建议是否也适用于Scala,或者是否有一个我缺少的更简洁的替代方案?
要使用Scala遵循此方法,我的代码将如下所示:
abstract class ApiClient(contentType: String, supportedContentTypes: List[String]) {
if (!(supportedContentTypes contains contentType)) {
throw new RuntimeException("Type " + contentType + " not supported")
}
}
class FacebookClient(contentType: String) extends ApiClient(
contentType,
List("text/xml", "application/xml")) {
}
val api = new FacebookClient("text/xml") // Runs!
这是最好的方法吗?我没有看到任何相反的例子,但加载像这样的超级构造函数对我来说并不“味道好”。感激不尽的任何想法!
答案 0 :(得分:2)
我认为最简单的解决方案是让supportedContentTypes
懒惰FacebookClient
:
class FacebookClient(contentType: String) extends ApiClient(contentType) {
override lazy val supportedContentTypes = List("text/xml", "application/xml")
}
这应该按预期工作。
你也可以使用抽象方法 - 它应该也可以正常工作。但与Java相比,涉及的语法要少得多。您通常需要将val
更改为def
,然后就完成了。