2016-12-10 14 views
1

Dies ist, was ich tun will -Scala: eine Variable einmal ohne val Zuordnung

class A(some args) { 
    var v: SomeType = null 
    def method1(args) = { 
    v = something1 
    ... 
    method3 
    } 

    def method2(args) = { 
    v = something2 
    ... 
    method3 
    } 
    def method3 = { 
    // uses v 
    } 
} 

In diesem speziellen Fall method1 und 2 sich gegenseitig aus und einer von ihnen wird genau einmal in der Lebensdauer eines genannt Instanz von A. Außerdem wird v einmal zugewiesen. Ich würde es lieber ein val machen. Aber da ich den Kontext von method2 oder method3 benötige, um v zu initialisieren, kann ich das im Konstruktor nicht tun.

Wie kann dieses "val" Verhalten erreicht werden? Ich kann daran denken, Methode1 und Methode2 zu ändern, um Methoden anzuwenden, aber ich mag die Idee nicht. Darüber hinaus haben Methode 1 und 2 dieselbe Argumentsignatur (daher würden für die Unterscheidung der zwei Arten von Anrufen einige weitere Informationen benötigt).

+1

Warum kann der 'v' Wert nicht als Argument an' m3' übergeben werden? Wird 'm3' von außerhalb der' Klasse' aufgerufen? Wenn ja, kann 'm3' vor entweder' m1' oder 'm2' genannt werden? (Was wie ein Designfehler klingt.) – jwvh

+0

Klingt für mich so, als müssten Sie nur erzwingen, dass 'method1' oder' method2' nur einmal aufgerufen werden können und dass 'method3' nur danach aufgerufen werden kann. –

+0

@jwvh m3 ist eine private Methode, sollte das in meinem Snippet erwähnt haben. – anindyaju99

Antwort

7

Eine wichtige Frage ist: Was genau nennen Sie "Val-Verhalten"? Für mich ist "val behaviour" das wird sofort bei der Deklaration zugewiesen, was statisch erzwungen werden kann. Sie scheinen zu erzwingen, dass v nicht doppelt vergeben wird. Sie möchten möglicherweise auch erzwingen ist nie lesen, bevor es zugewiesen ist.

final class OnceBox[A] { 
    private[this] var value: Option[A] = None 

    def update(v: A): Unit = { 
    if (value.isDefined) 
     throw new IllegalStateException("Value assigned twice") 
    value = Some(v) 
    } 

    def apply(): A = { 
    value.getOrElse { 
     throw new IllegalStateException("Value not yet assigned") 
    } 
    } 
} 

und jetzt Ihr Snippet:

class A(some args) { 
    val v = new OnceBox[SomeType] 
    def method1(args) = { 
    v() = something1 
    ... 
    method3 
    } 

    def method2(args) = { 
    v() = something2 
    ... 
    method3 
    } 
    def method3 = { 
    // uses v 
    v() 
    } 
} 

Oh, und nur ein Scherz, aber Ozma hat Single-Zuordnung vals Einbau-:-P

Sie könnten eine sehr kleine Helfer-Box für das erstellen
3

So etwas wie dieses vielleicht:

class A private (mtdType: Int, ...) { 
     val v = mdtType match { 
     case 1 => method1(...) 
     case 2 => method2(...) 
     } 
    } 


    object A { 
    def withMethod1(...) = new A(1, ...) 
    def withMethod2(...) = new A(2, ...) 
    } 

Oder eine andere Möglichkeit:

sealed trait A { 
    val v 
    def method3 = println(v) 
    } 

    class Method1(...) extends A { 
     val v = method1(...) 
    } 

    class Method2(...) extends A { 
     val v = method2(...) 
    } 
5

Ähnliche Idee zu der anderen Antwort, aber statt Subtypen, ein Feld.

scala> :pa 
// Entering paste mode (ctrl-D to finish) 

class A { 
    private[this] var context: Int = _ 
    lazy val v: String = 
    context match { 
     case 1 => "one" 
     case 2 => "two" 
     case _ => ??? 
    } 
    def m1() = { context = 1 ; v } 
    def m2() = { context = 2 ; v } 
} 

// Exiting paste mode, now interpreting. 

defined class A 

scala> val a = new A 
a: A = [email protected] 

scala> a.m2 
res0: String = two 

scala> a.m1 
res1: String = two 
+0

"v" wirft, bis der Kontext festgelegt ist, und funktioniert dann normal. –

+0

Ich habe diese Idee früher betrachtet. Aber das Problem mit meinem speziellen Anwendungsfall ist, dass es eine komplexe Initialisierung ist (viele Variablen, die in m1 und m2 berechnet werden). Der Kontext muss also eine Menge von Werten sein (kann eine Klasse mit den benötigten Feldern sein). Natürlich kann ich diesen Ansatz erweitern und sagen: v und Kontext sind vom selben Typ und wenn sie zugewiesen werden, resultieren sie auch auf dem gleichen Objekt. Aber das ist viel zu komplex, um zukünftige Devs zu erzwingen, dass sie v anstelle von Kontext verwenden. – anindyaju99