2011-10-06 15 views
5

że mamy klasę tak:wykonywania kodu w przeciążonego konstruktora przed wywołaniem tej()

import java.net.URL 
import xml._ 

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

i chcemy wykonać jakiś kod przed wywołaniem this (XML.load(url)) - mówią przetestować go z try. Można by oczekiwać, że pisząc coś jak to będzie działać:

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

ale nie będzie, bo Scala wymaga, aby dokonać połączenia do this() pierwsze pismo w przeciążonego konstruktora iw tym przypadku try staje się pierwszą wypowiedź .

Jakie byłoby rozwiązanie tego problemu?

Odpowiedz

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

Bardziej ogólnie, oceny argumentów musi się zdarzyć przed wywołaniem konstruktora, więc trzeba zrobić, że tam (blok w Scala jest wyrazem, ale napisać procedurę, zwykle napisane w obiekcie towarzysz , jeśli to będzie zbyt długie). Nie możesz tego zrobić, aby ten kod wybrał inny konstruktor, do którego zadzwonisz. Ale ponieważ wszystkie z nich muszą dotrzeć do pierwotnego, nie tracisz zbyt wiele. Poza tym potrzebujesz innego konstruktora, który wywołujesz, aby mieć przynajmniej jeden argument. Jeżeli istnieje kilka konstruktorów, podstawowym jeden nie powinien być jedynym bez argumentów (patrz Scala problem optional constructor)

+0

Dobrym rozwiązaniem dla deklarowanego problemu, ale to nie jest ogólnie . Prawdopodobnie to moja wina, że ​​ogłosiłem myląco banalny problem. W każdym razie sprawdź http://stackoverflow.com/questions/7680442/executing-code-in-overloaded-constructor-prior-to-calling-this/7687567#7687567 co nieco ewoluuje twoje rozwiązanie –

5

Sposób fabryka w obiekcie towarzysz:

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

Nie tylko upraszcza konstrukcję, ale można oszczędzić słowo kluczowe new.

+1

Nie widzę, jak to upraszcza projekt. Zmusza cię to również do wprowadzenia niespójności przez skonstruowanie obiektów zarówno ze słowem kluczowym new, jak i bez niego w zależności od scenariusza lub naruszeniem reguły DRY i tworzenia aliasów towarzyszących dla wszystkich konstruktorów w twoich klasach, włącznie z głównymi, abyś mógł konstruować bez 'nowego ' zawsze. A jednak wciąż będziesz musiał tworzyć klasy biblioteki z 'new'. –

+0

W rzeczywistości wzorzec jest dość powszechny w bibliotece scala i automatycznie implementowany dla klas przypadków. Więc "niekonsekwencja" już istnieje. Plus silne wady, które konstruktor zbyt często mówi klientowi, a mianowicie dokładny typ wyniku i fakt, że jest on nowo utworzony.Fabryka nie jest warta zrobienia przez cały czas, ale często są one wygodne i choć przeszkadza mi to, nie poprzestanę na niespójności. Pamiętam Delphi, gdzie konstruktorzy i funkcje statyczne są wywoływane tak samo od klienta. –

+0

Całkowicie się z Tobą zgadzam. Właściwie w [Kotlin] (http://confluence.jetbrains.net/display/Kotlin/Classes+and+Inance) rozwiązują ten problem, pozbywając się w ogóle słowa kluczowego "new". To dziwne, że faceci Scali nie widzą tutaj problemu. –

3

Rozwiązanie firmy didierd rozwiązuje zadeklarowany problem i jest nieco bliskie temu rozwiązaniu, ale nadal nie rozwiązuje problemu, gdy trzeba wykonać kilka instrukcji przed wywołaniem this. Ten zapewnia podejście ogólne do wszystkich scenariuszach:

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

Sztuką jest to, że this jest zasilany wyniku realizacji anonimowej funkcji w organizmie z której są dopuszczone do niczego.

Ale to działa tylko, gdy masz jednego argumentu głównego konstruktora - w innych sytuacjach trzeba będzie wprowadzić Tuple opartych Obejście:

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

Myślę, że moje komentarze dotyczyły twojego pierwszego przypadku z println. Ale w drugim przypadku robisz bardzo dobry punkt. –

Powiązane problemy