如何验证 Scala 抽象父对象中的子类字段?

发布于 2024-12-05 12:14:16 字数 1385 浏览 0 评论 0原文

我相信在 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 中被广泛讨论(例如 这里这里),一般答案是把父类构造函数中的“抽象字段”。这个建议也适用于 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!

这是最好的方法吗?我还没有看到任何相反的例子,但是像这样加载超级构造函数对我来说“闻起来”不太好。任何想法都感激不尽!

I believe that in Scala, like in Java, subclass fields are initialized after the super constructor has executed. Given this, I'm struggling to identify how best to create "abstract fields" that can be initialized in my subclass but validated (or used for validation of other fields) in the constructor of my abstract parent class. To give a simple example of what doesn't work:

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

This question is much-discussed for Java (e.g. here and here) and the general answer is to put the "abstract fields" in the parent class's constructor. Does this advice hold true for Scala too, or is there a neater alternative I'm missing?

To follow this approach with Scala, my code would look like this:

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!

Is this the best approach? I haven't seen any examples to the contrary but loading up the super constructor like this doesn't "smell" great to me. Any thoughts gratefully received!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

颜漓半夏 2024-12-12 12:14:16

我认为最简单的解决方案是使 FacebookClient 中的 supportedContentTypes 变得懒惰:

class FacebookClient(contentType: String) extends ApiClient(contentType) {
  override lazy val supportedContentTypes = List("text/xml", "application/xml")
}

这应该按预期工作。


您还可以使用抽象方法 - 它也应该可以正常工作。但与 Java 相比,涉及的语法要少得多。您通常需要将 val 更改为 def 就完成了。

I think that the easiest solution is to make supportedContentTypes in FacebookClient lazy:

class FacebookClient(contentType: String) extends ApiClient(contentType) {
  override lazy val supportedContentTypes = List("text/xml", "application/xml")
}

This should work as expected.


You can also use abstract method - it should also work just fine. But in contrast to Java, much less syntax is involved. You generally need to change val to def and you are done.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文