2013-10-17 13 views
9

Supponiamo di disporre di un costruttore di classi che accetta parametri con valore predefinito.Scala fornisce facoltativamente il parametro predefinito denominato

class A(val p1 : Int = 3, val p2 : Int = 4) 

Diciamo che non ho il controllo su questa classe e non posso modificarlo in alcun modo. Quello che voglio fare è chiamare il costruttore di A con p1 = 5, p2 = (se condition1 == true quindi 5 altro valore predefinito). Un modo per farlo è

if(condition1) 
    x = new A(5,5) 
else 
    x = new A(5) 

Come si può vedere, questo può facilmente ottenere grande se ci sono molti parametri e ciascuno devono essere forniti in modo condizionale. Quello che voglio è qualcosa come

x = new A(p1 = 5, p2 = <if condition1 = true then 5 else default>) 

Come posso farlo? Si noti che i campi in classe A sono vals, così non posso cambiarle dopo un'istanza A.

+0

E 'costoso per creare un usa e getta 'A' senza argomenti in modo da poter leggere tutti i suoi valori di default e usarle in seguito in' if' espressioni? –

+0

Ho pensato a questa soluzione. Non è costoso ma si sente un po 'hacky. Vorrei evitare di leggere prima i parametri predefiniti, se possibile. – DSR

+0

Dai un'occhiata all'ultima sezione, "Argomenti predefiniti" su http://docs.scala-lang.org/sips/completed/named-and-default-arguments.html. Sembra che puoi chiamare i metodi per ottenere i valori predefiniti. A proposito, se vieni con qualcosa di buono, per favore rispondi alla tua stessa domanda! –

risposta

1

Mi sembra si hanno tre possibilità:

  1. creare le variabili per contenere ciascuno dei valori che si desidera specificare, fare tutto il codice per inserire i valori e creare un'istanza A una volta alla fine. Ciò richiede conoscere i valori predefiniti, come menzionato da Ionut. Non penso che la creazione di un oggetto usa e getta per leggere le impostazioni predefinite sia tutto ciò che hackish - certamente non tanto quanto incorporare le impostazioni predefinite - ma qualunque cosa.

  2. utilizzare l'API di riflessione per creare A. Non sono esattamente sicuro di come farlo ma quasi certamente è possibile passare un elenco di parametri, con i parametri non specificati come predefiniti. Questo richiede Scala 2.10; prima, era disponibile solo l'API di reflection Java e dovevi hackerare l'implementazione interna dei parametri opzionali, che è hackish.

  3. Utilizzare le macro. Anche 2.10+. Penso che quasiquotes dovrebbe rendere possibile farlo senza troppe difficoltà, anche se non ho molta familiarità con loro, quindi non posso dirlo con certezza.

0

Ecco la risposta rapida e una sorta di sbagliato:

x = new A(p1 = 5, if (condition1) 5 else A.$lessinit$greater$default$2) 

La parte "sbagliata" è che questo funziona in 2.10, ma non in 2.9. Il nome magico per il metodo predefinito è cambiato da versione a versione (in particolare, tra 2,9 e 2,10), quindi è più sicuro cercare il suo nome e chiamarlo tramite riflessione. Vedi Instantiating a case class with default args via reflection, How do I access default parameter values via Scala reflection? e Scala dynamic instantiation with default arguments.

0

Cosa ne dite di una classe derivata da A in questo modo:

class D(val t2: (Boolean, Int)) extends A { 
    override val p2: Int = if(t2._1) t2._2 else A.init$default$2 
} 

Ora è possibile creare un'istanza A in questo modo:

x = new D((condition1, 5)) 

Se si dispone di più parametri, quindi è possibile aggiungere un override di simile dichiarazione e parametro di tupla per ciascuno.

0

non posso fare a sentire quello che chiediamo è un po 'irragionevole. Come già detto dovrebbe essere possibile con i macro. Ma generalmente, se non sei soddisfatto del costruttore/fabbrica fornito, allora devi scrivere che sei il proprio wrapper/fabbrica.Personalmente penso che potremmo voler vedere un nuovo punto sull'intera questione dei valori predefiniti, degli oggetti Null e della classe Option. Tuttavia è possibile tagliare la caldaia, senza macro. Mettere un un booleana implicita nel pacchetto di utilità:

implicit class BooleanRich2(n : Boolean) { 
    def apply[T](v1: T, v2: T): T = if (n) v1 else v2 
} 

Poi dicono che vogliamo utilizzare la classe di seguito, che non possiamo modificare:

final class A(val p1: Int = 1, val p2: Int = 2, val p3: Int = 3, val p4: Int = 4){ 
    override def toString = s"p1: $p1, p2: $p2, p3: $p3, p4: $p4" 
} 

possiamo usarlo come segue:

var c1 = true 
var c2 = false 
def c3(s: String) = (s =="") 

val a = new A(c1(20, 1)) //p1: 20, p2: 2, p3: 3, p4: 4 
println(a) //p1: 20, p2: 2, p3: 3, p4: 4 

val b = new A(p3 = c2(20, 3), p4 = c3("")(20, 4)) 
println(b) //p1: 1, p2: 2, p3: 3, p4: 20 

val c = new A(p3 = c1(20, 3), p4 = c3("A non empty String")(20, 4)) 
println(c) //p1: 1, p2: 2, p3: 20, p4: 4 
1

recuperare i valori di default,

val defaultA = new A() 

Poi

val x = new A(p1 = 5, p2 = if (cond) 5 else defaultA.p2) 
Problemi correlati