2011-10-06 4 views
5

Digamos que tenemos una clase como esta:Ejecución de código en el constructor sobrecargado antes de llamar esta()

import java.net.URL 
import xml._ 

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

y queremos ejecutar algún código antes de llamar a this (XML.load(url)) - decirlo prueba con try. Uno esperaría que escribir algo como esto funcionaría:

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

pero no lo hará, porque Scala requiere que usted hace la llamada a this() la primera instrucción del constructor sobrecargado y en este caso try se convierte en la primera declaración .

¿Cuál sería la solución a este problema?

Respuesta

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

De manera más general, la evaluación de los argumentos tiene que ocurrir antes de la llamada al constructor, por lo que que hay (un bloque en Scala es una expresión, pero escribir una rutina, normalmente por escrito en el objeto acompañante , si va a ser demasiado largo). Lo que no puede hacer es que este código elija a qué otro constructor llama. Pero como todos deben encaminarse a la primaria, no pierdes mucho. Además, necesita que el otro constructor al que llama tenga al menos un argumento. Si hay varios constructores, el primario debe normalmente no sea el que no tiene un argumento (ver Scala problem optional constructor)

+0

Una buena solución para el problema declarado, pero no es en general . Probablemente es mi culpa que declare un problema engañosamente trivial. De todos modos echa un vistazo a http://stackoverflow.com/questions/7680442/executing-code-in-overloaded-constructor-prior-to-calling-this/7687567#7687567 que de alguna manera evoluciona tu solución –

5

Un método de fábrica en el objeto acompañante:

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/")) 

No sólo simplifica el diseño, pero se puede ahorrar la palabra clave new.

+1

No veo cómo se simplifica diseño. También te obliga a introducir incoherencias mediante la construcción de objetos con y sin la palabra clave 'new' según el escenario o violar la regla DRY y crear alias complementarios para todos los constructores en tus clases, incluyendo los principales para que puedas construir sin' nuevo' siempre. Y, sin embargo, siempre tendrás que construir clases de biblioteca con 'new'. –

+0

En realidad, el patrón es bastante común en la biblioteca scala y se implementa automáticamente para las clases de casos. Entonces, la _inconsistencia_ ya está allí. Además de los fuertes inconvenientes que el constructor le dice demasiado al cliente la mayor parte del tiempo, es decir, el tipo exacto del resultado y el hecho de que se acaba de crear.No vale la pena hacer una fábrica todo el tiempo, pero a menudo son convenientes y, aunque me molesta, no me detendría ante la incoherencia. Recuerdo con cariño Delphi donde los constructores y las funciones estáticas fueron llamados del mismo modo por el cliente. –

+0

Estoy totalmente de acuerdo con usted. De hecho, en [Kotlin] (http://confluence.jetbrains.net/display/Kotlin/Classes+and+Inheritance) resuelven este problema al deshacerse de la palabra clave 'new'. Es extraño para mí que los chicos de Scala no vean un problema aquí. –

3

Si bien la solución de didierd resuelve el problema declarado y es algo parecido a este, todavía no resuelve el problema cuando debe ejecutar varias instrucciones antes de llamar al this. Éste ofrece un enfoque general para todos los escenarios:

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

El truco aquí es que this se alimenta con resultado de ejecutar una función anónima en el cuerpo de los cuales se le permite hacer nada.

Pero esto sólo funciona cuando se tiene un constructor principal de un solo argumento - en otros escenarios que tendrá que introducir un Tuple solución basada en:

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) 
} 
+0

Creo que mis comentarios cubrieron su primer caso con println. Pero haces un muy buen punto con el segundo caso. –

Cuestiones relacionadas