8 Stimmen

Wie kann man in Scala einen lokalen Parameter im primären Konstruktor einer Klasse definieren?

Wie definiert man in Scala einen lokalen Parameter im primären Konstruktor einer Klasse, der kein Datenelement ist und der z.B. nur dazu dient, ein Datenelement in der Basisklasse zu initialisieren?

Wie kann ich zum Beispiel im folgenden Code den Parameter b im primären Konstruktor der Klasse B so dass es nur einen temporären lokalen Parameter und kein Datenelement erzeugt?

class A(var a: Int)
class B(?b?) extends A(b)

Randall, Ihre Antworten erklären, warum sich der Scala-Compiler beschwert, wenn ich eine Methode einführe inc die die Eigenschaft a , sondern ändern Sie auch den Namen des Parameters in der Klasse B Konstruktor mit dem des Parameters in der Klasse A Konstrukteur:

class A(var a: Int)
class B(a: Int) extends A(a) {
  def inc(value: Int) { this.a += value }
}

Scala-Compiler-Ausgabe:

$ scala construct.scala
construct.scala:3: error: reassignment to val
  def inc(value: Int) { this.a += value }
                               ^
one error found

Scala beschwert sich, weil die Klasse B muss jetzt eine private, schreibgeschützte Eigenschaft haben a aufgrund der Bezugnahme auf a en inc . Ändern von B(a: Int) à B(var a: Int) erzeugt einen anderen Compilerfehler:

construct.scala:2: error: error overriding variable a in class A of type Int;
 variable a needs `override' modifier
class B(var a: Int) extends A(a) {
            ^
one error found

Hinzufügen von override ist auch nicht hilfreich:

construct.scala:2: error: error overriding variable a in class A of type Int;
 variable a cannot override a mutable variable
class B(override var a: Int) extends A(a) {
                 ^
one error found

Wie kann ich denselben Namen im Parameter im primären Konstruktor von B als die im primären Konstruktor der Basisklasse definierte Eigenschaft A ?

16voto

Randall Schulz Punkte 26072

Wenn Sie das Schlüsselwort "var" oder "val" aus dem Konstruktorparameter entfernen, wird keine Eigenschaft erzeugt.

Beachten Sie jedoch, dass Konstruktorparameter, die nicht var, nicht val sind, in-scope und in der gesamten Klasse zugänglich sind. Wenn Sie einen solchen Parameter in Nicht-Konstruktor-Code (d.h. im Körper einer Methode) verwenden, gibt es ein unsichtbares privates Feld in der generierten Klasse, das diesen Konstruktorparameter enthält, genau wie bei einem "private var" oder "private val" Konstruktorparameter.

Nachtrag (besser spät als nie??):

In diesem Code kommen die Verweise auf den Konstruktorparameter nur im Konstruktorkörper vor:

class C1(i: Int) {
  val iSquared = i * i
  val iCubed = iSquared * i
  val iEven = i - i % 2
}

... Hier der Wert i existiert nur während der Ausführung des Konstruktors.

Im folgenden Code muss der Konstruktorparameter jedoch in ein (privates) Feld der generierten Klasse kopiert werden, da der Konstruktorparameter in einem Methodenrumpf referenziert wird, der nicht Teil des Konstruktorkörpers ist (was den Speicherbedarf um die 4 Bytes erhöht, die für die Aufnahme eines Int ):

class C2(i: Int) {
  val iSquared = i * i
  val iCubed = iSquared * i
  val iEven = i - i % 2

  def mod(d: Int) = i % d
}

4voto

Derek Mahar Punkte 26470

Nach einigen Experimenten habe ich festgestellt, dass das einfache Weglassen var o val vor dem Parameter b wird es zu einem lokalen Parameter und nicht zu einem Datenelement:

class A(var a: Int)
class B(b: Int) extends A(b)

Java-Erweiterung:

$ javap -private B
Compiled from "construct.scala"
public class B extends A implements scala.ScalaObject{
    public B(int);
}

$ javap -private A
Compiled from "construct.scala"
public class A extends java.lang.Object implements scala.ScalaObject{
    private int a;
    public A(int);
    public void a_$eq(int);
    public int a();
    public int $tag()       throws java.rmi.RemoteException;
}

Beachten Sie, dass die Klasse A hat ein privates Datenelement a aufgrund der var a: Int in seinem primären Konstruktor. Klasse B hat jedoch keine Datenelemente, aber sein primärer Konstruktor hat immer noch einen einzelnen Integer-Parameter.

4voto

ziggystar Punkte 27565

Sie können temporäre Variablen während der Initialisierung von einzelnen Klassenmitgliedern wie folgt erstellen:

class A(b:Int){
  val m = {
    val tmp = b*b
    tmp+tmp
  }
}

3voto

Randall Schulz Punkte 26072

Derek,

Wenn Sie das haben:

class A(a: Int) {
  val aa = a // reference to constructor argument in constructor code (no problem)
  def m: Float = a.toFloat // reference to constructor argument in method body (causes a to be held in a field)
}

finden Sie (z. B. mit javap) heraus, dass ein Feld namens "a" in der Klasse vorhanden ist. Wenn Sie das "def m" auskommentieren, werden Sie feststellen, dass das Feld nicht erstellt wird.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X