Выполнение кода в перегруженном конструкторе перед вызовом this () - PullRequest
5 голосов
/ 07 октября 2011

Скажем, у нас есть такой класс:

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 ]

6 голосов
/ 07 октября 2011
def this(url: Url) = this(try {XML.load(url)} catch {case _ => <results/>})

В более общем смысле, оценка аргументов должна происходить до вызова конструктора, поэтому вы делаете это ( блок в scala является выражением , но вы пишете подпрограмму, обычно написанную в объекте-компаньоне , если это будет слишком долго). То, что вы не можете сделать, это заставить этот код выбирать, какой другой конструктор вы вызываете. Но так как все они должны проложить маршрут к первичному, вы не потеряете много. Кроме того, вам нужен другой вызываемый вами конструктор, чтобы иметь хотя бы один аргумент. Если имеется несколько конструкторов, первичным обычно не должен быть тот без аргумента (см. Необязательный конструктор задачи Scala )

5 голосов
/ 07 октября 2011

Заводской метод в сопутствующем объекте:

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.

3 голосов
/ 07 октября 2011

Хотя решение 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)
}
...