在调用this()之前在重载的构造函数中执行代码

时间:2011-10-06 21:05:53

标签: scala constructor overloading

假设我们有这样一个类:

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成为第一个语句。

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

3 个答案:

答案 0 :(得分:6)

def this(url: Url) = this(try {XML.load(url)} catch {case _ => <results/>})

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

答案 1 :(得分:5)

伴侣对象中的工厂方法:

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关键字。

答案 2 :(得分: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)
}