【问题标题】:Executing code in overloaded constructor prior to calling this()在调用 this() 之前在重载的构造函数中执行代码
【发布时间】:2011-10-06 21:05:53
【问题描述】:

假设我们有这样一个类:

import java.net.URL
import xml._

class SearchData(xml: Node) {
  def this(url: URL) = this (XML.load(url))
}

我们想在调用this (XML.load(url)) 之前执行一些代码——比如用try 测试它。人们会期望写这样的东西会起作用:

class SearchData(xml: Node) {
  def this(url: URL) {
    try {
        this (XML.load(url))
    } catch {
      case _ => this(<results/>)
    }
  }
}

但它不会,因为 Scala 要求您调用 this() 是重载构造函数中的第一条语句,在这种情况下,try 成为第一条语句。

那么解决这个问题的方法是什么?

【问题讨论】:

    标签: scala constructor overloading


    【解决方案1】:
    def this(url: Url) = this(try {XML.load(url)} catch {case _ => <results/>})
    

    更一般地说,参数的计算必须在构造函数调用之前发生,所以你在那里(scala中的一个块是一个表达式,但是写一个例程,通常写在同伴中对象,如果它太长)。你不能做的是让这段代码选择你调用的其他构造函数。但是由于它们都必须路由到主要的,因此您不会损失太多。此外,您需要调用的另一个构造函数至少有一个参数。如果有多个构造函数,主构造函数通常不应是没有参数的构造函数(参见Scala problem optional constructor

    【讨论】:

    • 对声明的问题的一个很好的解决方案,但它不是一般的。可能是我的错,我宣布了一个误导性的微不足道的问题。无论如何,请查看stackoverflow.com/questions/7680442/…,这在一定程度上改进了您的解决方案
    【解决方案2】:

    伴随对象中的工厂方法:

    object SearchData {
      def apply(xml: Node) = new SearchData(xml) //Added to provide uniform factories
      def apply(url: URL) = {
        try {
          new SearchData(XML.load(url))
        } catch {
          case _ => new SearchData(<results/>)
        }
      }
    }
    
    //Example
    val sd = SearchData( new URL( "http://example.com/" ) )
    

    它不仅简化了设计,还可以省去new 关键字。

    【讨论】:

    • 我看不出它如何简化设计。它还迫使您通过根据场景构造带有和不带有 new 关键字的对象来引入不一致,或者违反 DRY 规则并为您的类中的所有构造函数创建伴随别名(包括主要构造函数),以便您可以在没有 new 的情况下构造总是。然而,您仍然必须使用new 构建库类。
    • 实际上该模式在 scala 库中很常见,并且为案例类自动实现。所以不一致已经存在了。加上构造函数在大多数情况下告诉客户端太多的缺点,即结果的确切类型和它是新创建的事实。工厂不值得一直做,但它们通常很方便,虽然它确实让我烦恼,但我不会因为不一致而停下来。我记得在 Delphi 中,构造函数和静态函数的调用方式与客户端的调用方式相同。
    • 我完全同意你的看法。实际上,在Kotlin 中,他们完全摆脱了new 关键字来解决这个问题。对我来说很奇怪 Scala 的人在这里没有看到问题。
    • 它简化了设计,因为只有一种构建对象的方法: 构造函数。所以先调用它没问题……那种工厂只是语法糖,可以让用户免去额外的强制性步骤。顺便说一句,我更新了代码以解决 不一致
    【解决方案3】:

    虽然 didierd 的解决方案解决了声明的问题并且在某种程度上接近这个问题,但当您必须在调用 this 之前执行多个语句时,它仍然不能解决问题。这为所有场景提供了一种通用方法:

    class SearchData(xml: Node) {
      def this(url: URL) = this {
        println(url)
        try {
          XML.load(url)
        } catch {
          case _ => <results/>
        }
      }
    }
    

    这里的诀窍是this 会得到执行一个匿名函数的结果,你可以在它的主体中做任何事情。

    但这仅在您拥有单参数主构造函数时才有效 - 在其他情况下,您将不得不引入基于 Tuple 的解决方法:

    class SearchData(xml: Node, valid: Boolean) {
      def this(url: URL) = this {
        println(url)
        try {
          (XML.load(url), true)
        } catch {
          case _ => (<results/>, false)
        }
      }
      def this(t: (Node, Boolean)) = this(t._1, t._2)
    }
    

    【讨论】:

    • 我想我的 cmets 用 println 覆盖了你的第一个案例。但你对第二种情况提出了很好的观点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-21
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多