Say we have a class like this:
import java.net.URL
import xml._
class SearchData(xml: Node) {
def this(url: URL) = this (XML.load(url))
}
and we want to execute 开发者_JAVA技巧some code prior to calling this (XML.load(url))
- say test it with try
. One would expect that writing something like this would work:
class SearchData(xml: Node) {
def this(url: URL) {
try {
this (XML.load(url))
} catch {
case _ => this(<results/>)
}
}
}
but it won't, because Scala requires that you make the call to this()
the first statement in the overloaded constructor and in this case try
becomes the first statement.
So what would be the solution to this problem?
def this(url: Url) = this(try {XML.load(url)} catch {case _ => <results/>})
More generally, the evaluation of the arguments has to happen before the constructor call, so you do that there (a block in scala is an expression, but write a routine, typically written in the companion object, if it is going to be too long). What you cannot do is have this code choose which other constructor you call. But as all of them must route to the primary one, you do not lose much. Also, you need the other constructor you call to have at least one argument. If there are several constructors, the primary one should normally not be the one without an argument (see Scala problem optional constructor)
A factory method in companion object:
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/" ) )
Not only it simplifies design, but you can spare the new
keyword.
While didierd's solution solves the declared problem and is somewhat close to this one, it still doesn't solve the problem when you have to execute several statements prior to calling this
. This one provides a general approach to all scenarios:
class SearchData(xml: Node) {
def this(url: URL) = this {
println(url)
try {
XML.load(url)
} catch {
case _ => <results/>
}
}
}
The trick here is that this
is fed with result of executing an anonymous function in body of which you are allowed to do anything.
But this only works when you have a single-argument main constructor - in other scenarios you will have to introduce a Tuple
-based workaround:
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)
}
精彩评论