2013-10-17 11 views
9

Angenommen, wir haben einen Klassenkonstruktor, der Parameter mit einem Standardwert akzeptiert.Scala liefert optional den Standardparameter

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

Sagen wir, ich habe keine Kontrolle über diese Klasse und kann sie nicht ändern. Ich möchte den Konstruktor von A aufrufen, mit p1 = 5, p2 = (wenn Bedingung1 == wahr ist, dann 5 anderer Standardwert). Eine Möglichkeit, dies zu tun ist,

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

Wie Sie sehen können, dies leicht groß werden kann, wenn es viele Parameter sind und jeder muss bedingt geliefert werden. Was ich will ist etwas wie

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

Wie kann ich das tun? Beachten Sie, dass die Felder in der Klasse A sind vals, so dass ich kann nicht sie ändern, nachdem A. Instanziieren

+0

Ist es teuer, eine Wegwerf 'A' ohne Argumente zu erstellen, so dass Sie alle die Standardwerte lesen und sie später in' if' Ausdrücke verwenden? –

+0

Ich dachte über diese Lösung nach. Es ist nicht teuer, aber fühlt sich ein wenig hacky. Ich möchte vermeiden, erste Standardparameter zu lesen, wenn möglich. – DSR

+0

Werfen Sie einen Blick auf den letzten Abschnitt "Standardargumente" auf http://docs.scala-lang.org/sips/completed/named-and-default-arguments.html. Offenbar können Sie Methoden aufrufen, um die Standardwerte zu erhalten. BTW, wenn Sie etwas Gutes finden, bitte beantworten Sie Ihre eigene Frage! –

Antwort

1

Es scheint mir, haben Sie drei Möglichkeiten:

  1. Variablen Erstellen Sie jeden der Werte halten Sie wollen Geben Sie den gesamten Code zum Ausfüllen der Werte ein und instanziieren Sie A einmal am Ende. Dies erfordert die Kenntnis der Standardwerte, wie Ionut erwähnt. Ich denke nicht, dass das Erstellen eines Wegwerfobjekts zum Lesen der Standardeinstellungen alles hackisch ist - sicherlich nicht so sehr wie das Einbetten der Standardeinstellungen selbst - aber was auch immer.

  2. Verwenden Sie die Reflektions-API, um A zu erstellen. Ich weiß nicht genau, wie ich das machen soll, aber mit ziemlicher Sicherheit kann man eine Liste von Parametern übergeben, wobei nicht spezifizierte Parameter voreingestellt sind. Dies erfordert Scala 2.10; davor war nur die Java-Reflection-API verfügbar, und Sie müssten die interne Implementierung von optionalen Parametern hacken, die hackish ist.

  3. Verwenden Sie Makros. Auch 2.10+. Ich denke, dass Quasiquoten es ermöglichen sollten, dies ohne große Schwierigkeiten zu tun, obwohl ich sie nicht so gut kenne, so kann ich nicht sicher sagen.

0

Hier ist die schnelle und Art falsche Antwort:

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

Der "falsche" Teil ist, dass dies in 2.10 funktioniert, aber nicht in 2.9. Der magische Name für die Standardmethode hat sich von Version zu Version geändert (insbesondere zwischen 2.9 und 2.10), so dass es sicherer ist, seinen Namen nachzuschlagen und ihn per Reflektion aufzurufen. Siehe Instantiating a case class with default args via reflection, How do I access default parameter values via Scala reflection? und Scala dynamic instantiation with default arguments.

0

Was eine abgeleitete Klasse von A wie dies etwa mit:

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

Jetzt Sie A wie folgt instanziieren:

x = new D((condition1, 5)) 

Wenn Sie mehr Parameter haben, dann können Sie eine ähnliche Überschreibung hinzufügen Anweisungs- und Tupelparameter für jeden.

0

Ich kann nicht helfen zu fühlen, was Sie fragen, ist ein bisschen unzumutbar. Wie bereits gesagt, sollte es mit Makros möglich sein. Aber im Allgemeinen, wenn Sie nicht mit dem zur Verfügung gestellten Konstruktor/Fabrik zufrieden sind, dann müssen Sie Ihre eigene Wrapper/Fabrik schreiben.Persönlich denke ich, dass wir das Problem der Standardwerte, der Null-Objekte und der Option-Klasse noch einmal aufgreifen wollen. Es ist jedoch möglich, einen Textbaustein ohne Makros auszuschneiden. Legen Sie eine implizite Boolean in Ihrem Utility-Paket:

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

Dann sagen wir die folgende Klasse verwenden möchten, die wir nicht ändern können:

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" 
} 

Wir können es verwenden, wie folgt:

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

die Standardwerte holen,

val defaultA = new A() 

Dann

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