Warum unterscheiden sich "privater Wert" und "privater Endwert"?

100

Früher habe ich das gedacht private valund private final valbin es auch, bis ich Abschnitt 4.1 in Scala Reference sah:

Eine konstante Wertedefinition hat die Form

final val x = e

wobei e ein konstanter Ausdruck ist (§6.24). Der letzte Modifikator muss vorhanden sein und es dürfen keine Typanmerkungen angegeben werden. Verweise auf den konstanten Wert x werden selbst als konstante Ausdrücke behandelt; im generierten Code werden sie durch die rechte Seite der Definition ersetzt. e.

Und ich habe einen Test geschrieben:

class PrivateVal {
  private val privateVal = 0
  def testPrivateVal = privateVal
  private final val privateFinalVal = 1
  def testPrivateFinalVal = privateFinalVal
}

javap -c Ausgabe:

Compiled from "PrivateVal.scala"
public class PrivateVal {
  public int testPrivateVal();
    Code:
       0: aload_0       
       1: invokespecial #19                 // Method privateVal:()I
       4: ireturn       

  public int testPrivateFinalVal();
    Code:
       0: iconst_1      
       1: ireturn       

  public PrivateVal();
    Code:
       0: aload_0       
       1: invokespecial #24                 // Method java/lang/Object."<init>":()V
       4: aload_0       
       5: iconst_0      
       6: putfield      #14                 // Field privateVal:I
       9: return
}

Der Bytecode ist genau so, wie Scala Reference sagte: private valnicht private final val.

Warum behandelt Scalac nicht einfach private valso private final val? Gibt es einen Grund dafür?

Yang Bo
quelle
28
Mit anderen Worten: Da a valbereits unveränderlich ist, warum brauchen wir das finalSchlüsselwort überhaupt in Scala? Warum kann der Compiler nicht alle vals genauso behandeln wie final vals?
Jesper
Beachten Sie, dass der privateBereichsmodifikator dieselbe Semantik wie package privatein Java hat. Sie können sagen private[this].
Connor Doyle
5
@ConnorDoyle: Als Paket privat? Ich denke nicht: privatebedeutet, dass es nur für Instanzen dieser Klasse sichtbar ist, private[this]nur für diese Instanz - mit Ausnahme von Instanzen derselben Klasse kann privateniemand (einschließlich aus demselben Paket) auf den Wert zugreifen.
Make42

Antworten:

81

Dies ist also nur eine Vermutung, aber es war in Java immer wieder ärgerlich, dass endgültige statische Variablen mit einem Literal auf der rechten Seite als Konstanten in Bytecode eingefügt werden. Dies führt zwar zu einem Leistungsvorteil, führt jedoch dazu, dass die binäre Kompatibilität der Definition unterbrochen wird, wenn sich die "Konstante" jemals ändert. Bei der Definition einer endgültigen statischen Variablen, deren Wert möglicherweise geändert werden muss, müssen Java-Programmierer auf Hacks wie das Initialisieren des Werts mit einer Methode oder einem Konstruktor zurückgreifen.

Ein Wert in Scala ist im Java-Sinne bereits endgültig. Es sieht so aus, als würden die Designer von Scala den redundanten Modifikator final verwenden, um "Erlaubnis zum Inline des konstanten Werts" zu bedeuten. Scala-Programmierer haben also die vollständige Kontrolle über dieses Verhalten, ohne auf Hacks zurückgreifen zu müssen: Wenn sie eine Inline-Konstante wünschen, einen Wert, der sich nie ändern sollte, aber schnell ist, schreiben sie "final val". Wenn sie Flexibilität wünschen, um den Wert zu ändern, ohne die Binärkompatibilität zu beeinträchtigen, einfach "val".

Steve Waldman
quelle
9
Ja, das ist der Grund für nicht private Werte, aber private Werte können offensichtlich nicht in andere Klassen eingefügt werden und die Kompatibilität auf die gleiche Weise beeinträchtigen.
Alexey Romanov
3
Gibt es eine binäre Kompatibilität Problem , wenn ich ändern private valzu private final val?
Yang Bo
1
@ Steve-Waldman Entschuldigung, meinten Sie valin Ihrem zweiten Absatz?
Yang Bo
1
Hier sind die Details zu endgültigen statischen Variablen in Java in Bezug auf die Binärkompatibilität - docs.oracle.com/javase/specs/jls/se7/html/…
Eran Medan
8

Ich denke, die Verwirrung hier entsteht durch die Verschmelzung von Unveränderlichkeit mit der Semantik von final. vals kann in untergeordneten Klassen überschrieben werden und kann daher nicht als endgültig behandelt werden, es sei denn, dies ist ausdrücklich als solches gekennzeichnet.

@Brian Die REPL bietet Klassenbereich auf Zeilenebene. Sehen:

scala> $iw.getClass.getPackage
res0: Package = package $line3

scala> private val x = 5
<console>:5: error: value x cannot be accessed in object $iw
  lazy val $result = `x`

scala> private val x = 5; println(x);
5
Connor Doyle
quelle
1
Ich spreche über private val. Kann es überschrieben werden?
Yang Bo
Nein, private Werte können nicht überschrieben werden. Sie können einen anderen privaten Wert mit demselben Namen in einer Unterklasse neu definieren, aber es ist ein völlig anderer Wert, der zufällig denselben Namen hat. (Alle Verweise auf den alten würden sich immer noch auf den alten beziehen.)
aij
1
Es scheint jedoch nicht nur dieses übergeordnete Verhalten zu sein, da ich beim Interpreter einen endgültigen Wert (oder sogar einen endgültigen Wert) erstellen kann, ohne überhaupt im Kontext einer Klasse zu stehen.
Nairbv