2016-12-14 2 views
1

Ich habe eine Klasse Door erstellt und möchte eine Funktion schreiben, die den offenen Status auf das ändert, was das Argument ist.Scala-Funktion zum Ändern des Attributs der Klasse

Das Folgende funktioniert nicht. Was wäre der beste Weg, das zu tun? (Entschuldigung für die Anfängerfrage.)

Antwort

2

Als Łukasz und Venkat Reddy Sudheer Aedama in ihren Kommentaren erwähnt, ist die empfohlene Methode, Werte in Scala zu mutieren ist unveränderliche Objekte zu verwenden.

Dies bedeutet, dass bei jeder Änderung des Objektstatus ein neues Objekt mit aktualisierten Werten erstellt wird. Es ist notwendig, eine referenzielle Transparenz zu erreichen, die es Programmen erlaubt, frei von Nebenwirkungen zu sein, was als eine wünschenswerte Eigenschaft angesehen wird, die den Code wesentlich einfacher zu pflegen macht.

In Scala gibt es einen bequemen Mechanismus namens Fallklassen. Schauen wir uns an, wie Door als Fall Klasse definiert werden könnten:

case class Door(name: String, open: Boolean) { 
    def opened(open: Boolean) = copy(open = open) 
} 

Wie Sie es bemerkt haben keine var oder val vor der Klasse Argumente. Dies liegt daran, dass in der Fallklasse jedes Argument als val angenommen wird, wodurch Fallklassen standardmäßig unveränderlich gemacht werden.

Ein weiterer Unterschied ist, dass statt einer neuen Instanz mit new Operator eine copy Methode verwendet wird, die es ermöglicht, die Werte der angegebenen Felder zu ändern. Es mag schwierig sein, den Vorteil zu sehen, dies jetzt so zu tun, aber es wird sehr praktisch, wenn Sie Fallklassen mit viel mehr Feldern behandeln.

Jetzt können Sie Door instanziiert und es auf folgende Weise aktualisieren:

val door = Door("MyDoor", false) 
door.name // "MyDoor" 
door.open // false 

val door2 = door.opened(true) 
door2.name // "MyDoor" 
door2.open // true 

Eine weitere Bequemlichkeit von Fallklassen zur Verfügung gestellt ist, dass man sie ohne new Schlüsselwort instanziieren, die für den Aufruf Door.apply("MyDoor", false) ein syntaktischer Zucker ist.

Bis jetzt haben wir gesehen, wie eine Fallklasse für unveränderliche Datenobjekte zu deklarieren und zu verwenden. Aber heißt das, dass wir unsere Klassen immer so deklarieren sollten?

Die wichtige Eigenschaft von Fallklassen ist, dass sie transparent sind. Was durch die Tatsache auferlegt wird, dass alle ihre Felder als öffentlich zugänglich sind val s. Dies ist nützlich, wenn Sie die Objekteigenschaften auf die gleiche Weise wie sie gespeichert werden möchten.

Auf der anderen Seite, wenn Sie die Interna Ihrer Objekte nicht offen legen wollen (also Ihre Klasse kann undurchsichtig genannt werden), um zum Beispiel einige Invarianten zu erzwingen, ist es vielleicht besser, Standard-Scala-Klassen zu verwenden .

Für allgemeine Regeln und Kompromisse im Zusammenhang mit Transparenz vs Opazität empfehle ich Li Haoyi Artikel Strategic Scala Style: Designing Datatypes lesen.


In einigen Fällen, zum Beispiel wenn Sie möchten, die Leistung verbessern oder Speicherbedarf Ihres Programms zu reduzieren, können Sie änderbare Klasse entscheiden, stattdessen zu verwenden:

class Door(var name: String, 
      var open: Boolean) { 
    def openDoor(newDoorState: Boolean): Unit = 
    open = newDoorState 
} 

val door = new Door("MyDoor",true) 
door.name // "MyDoor" 
door.open // true 

door.openDoor(false) 
door.name // "MyDoor" 
door.open // false 

Denken Sie daran, dass unveränderliches Objekt Im Allgemeinen bevorzugen Sie die Art und Weise, die Daten weiterzuleiten, und Sie sollten Ihre Programme standardmäßig als referenziell transparent gestalten. Wenn dies wirklich notwendig wird, sollten Sie solche Mikrooptimierungen wie die Änderung Ihrer Datenstrukturen als änderbar betrachten.

Auch ich werde hinzufügen, dass in Scala gibt es eine Konvention (die aus Java stammt), camel case für Klassenfelder und Methoden zu verwenden, so dass Sie Ihre Methode open_door wie im ursprünglichen Beispiel benennen, sollten Sie es openDoor nennen .

+2

wäre es wert, dass die OP zu erwähnen sollte 'camelCase' für Methodennamen verwenden, weist darauf hin, dass es in der Regel besser ist, die zu verwenden erste Variante über die änderbare, drop vars, wenn er sowieso ein neues Objekt erstellen und wahrscheinlich eine 'case class Door (name: String, open: Boolean)' erstellen und 'open'-Methode mit' copy' implementieren will. –

+1

Wie @ Łukasz oben erwähnt: 'Fall Klasse Tür (name: String, isOpen: Boolean) { def OPENDOOR (newDoorState: Boolean): Tür = this.copy (isOpen = newDoorState) }' –

+1

Łukasz, Venkat Reddy Sudheer Aedama Es tut mir leid für die späte Antwort, das war in der Tat eine faule Antwort. Danke für deine Anregungen, ich habe meine Antwort jetzt mit mehr Details erweitert. – adamwy

1

Ihre open_door-Funktion gibt eine neue Tür zurück. Stattdessen sollte es die open Attribut:

class Door(var name: String, var open: Boolean) { 
    def openDoor(newDoorState: Boolean) { 
    open = newDoorState 
    } 
    override def toString(): String = "(" + name + ", " + newDoorState + ")" 
} 
0

Ihre aktuelle open_door-Methode erstellt eine neue Instanz von Door, wobei open auf newDoorState gesetzt ist.

Um das zu erreichen, was Sie wollen, müssen Sie tun:

def open_door(newDoorState: Boolean): Unit { 
    open = newDoorState 
} 

(Man beachte die Abwesenheit von Gleichheitszeichen vor der Funktion Körper Außerdem können Sie Drop „: Einheit“ Teil - geschlossen werden wird..)

+3

mit dem '=' ist erforderlich. Die Prozedur-Syntax wurde vor einigen Jahren nicht mehr weiterentwickelt. https://issues.scala-lang.org/browse/SI-7605 – retrospectacus

+0

Ich sehe, danke für's Bemerken. Vor einem Jahr habe ich noch die Version von Scala benutzt, wo sie gültig war. – Ashalynd

0

Danke, bisher waren die Antworten alle objektorientiert. Ich füge eine funktionelle Antwort, also nicht den Original-Türklassenwechsel:

class Door{ 
     var name="MyDoor" 
     var open_status=true 
def this(name:String, open_status:Boolean){ 
    this() 
    this.open_status = open_status 
} 

override def toString = { 
"%s is open is %s.".format(name, open_status) 
} 
} 

println(new Door) 
println(new Door("MyDoor",false)) 
Verwandte Themen