2011-10-06 2 views
5

Supponiamo di avere una classe come questa:esecuzione di codice nel costruttore di overload prima di chiamare questo()

import java.net.URL 
import xml._ 

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

e vogliamo eseguire del codice prima di chiamare this (XML.load(url)) - dire di prova con try. Ci si aspetterebbe che scrivere qualcosa di simile a questo dovrebbe funzionare:

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

, ma non sarà così, perché Scala richiede che si effettua la chiamata al this() la prima istruzione nel costruttore sovraccaricato e in questo caso try diventa la prima dichiarazione .

Quindi quale sarebbe la soluzione a questo problema?

risposta

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

Più in generale, la valutazione degli argomenti deve accadere prima che la chiamata al costruttore, in modo da fare che c'è (un blocco in Scala è un'espressione, ma scrivere una routine, in genere scritta nell'oggetto compagna , se sarà troppo lungo). Quello che non puoi fare è avere questo codice scegliere quale altro costruttore chiami. Ma poiché tutti devono essere diretti a quello primario, non perdi molto. Inoltre, è necessario che l'altro costruttore che chiami abbia almeno un argomento. Se ci sono diversi costruttori, quello primario dovrebbe normalmente non essere l'uno senza un argomento (vedi Scala problem optional constructor)

+0

Una buona soluzione per il problema dichiarata, ma non è generale . Probabilmente è colpa mia se ho dichiarato un problema fuorviante di poco conto. In ogni caso, controlla http://stackoverflow.com/questions/7680442/executing-code-in-overloaded-constructor-prior-to-calling-this/7687567#7687567 che alquanto evolve la tua soluzione –

5

Metodo fabbrica in oggetto associato:

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

Non solo semplifica la progettazione, ma si può risparmiare la parola chiave new.

+1

Non vedo come si semplifica design. Inoltre, ti costringe a introdurre incoerenze costruendo oggetti con e senza la parola chiave 'new' a seconda dello scenario o violare la regola DRY e creare alias companion per tutti i costruttori nelle classi che includono quelli principali in modo che tu possa costruire senza' new' sempre. E tuttavia dovrai sempre costruire classi di librerie con 'new'. –

+0

In realtà il modello è abbastanza comune nella libreria di scala e viene implementato automaticamente per le classi dei casi. Quindi l'incostanza è già presente. Inoltre, i forti svantaggi che il costruttore dice troppo al cliente la maggior parte del tempo, vale a dire il tipo esatto del risultato e il fatto che è stato appena creato.Una fabbrica non vale la pena fare tutto il tempo, ma sono spesso convenienti e mentre mi dà fastidio, non mi fermerei all'inconsistenza. Ricordo con affetto Delphi dove costruttori e funzioni statiche venivano chiamati allo stesso modo dal client. –

+0

Sono d'accordo con te totalmente. In realtà in [Kotlin] (http://confluence.jetbrains.net/display/Kotlin/Classes+and+Inheritance) risolvono questo problema eliminando la parola chiave 'new'. È strano per me che i ragazzi di Scala non vedano un problema qui. –

3

Mentre soluzione di didierd risolve il problema dichiarato ed è un po 'vicino a questo, non risolve il problema quando si devono eseguire più istruzioni prima di chiamare this. Questo fornisce un approccio generale a tutti gli scenari:

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

Il trucco è che this è alimentato con risultato dell'esecuzione di una funzione anonima in corpo di cui si è permesso di fare qualsiasi cosa.

Ma questo funziona solo quando si dispone di un costruttore principale singolo argomento - in altri scenari si dovrà introdurre un Tuple soluzione basata su:

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

Penso che i miei commenti riguardassero il tuo primo caso con println. Ma tu fai un ottimo punto con il secondo caso. –