2012-09-06 6 views
26

Was ist der Grund für val s nicht (?) In Singleton-Objekten automatisch endgültig? Z.B.Warum ist ein `val` in einem` Objekt` nicht automatisch final?

object NonFinal { 
    val a = 0 
    val b = 1 

    def test(i: Int) = (i: @annotation.switch) match { 
     case `a` => true 
     case `b` => false 
    } 
} 

ergibt:

<console>:12: error: could not emit switch for @switch annotated match 
      def test(i: Int) = (i: @annotation.switch) match { 
                ^

Während

object Final { 
    final val a = 0 
    final val b = 1 

    def test(i: Int) = (i: @annotation.switch) match { 
     case `a` => true 
     case `b` => false 
    } 
} 

Kompiliert ohne Warnungen, so erzeugt vermutlich die schnelle Musterabgleich-Tabelle.

Hinzufügen final scheint pure nerviger Lärm für mich. Ist kein object per se endgültig und damit auch seine Mitglieder?

+0

Hm. Was ist mit Eigenschaften, die verwendet werden könnten? –

+0

@TonyK. - Was meinen Sie? Selbst wenn ich "Merkmal T {def a: Int}" hätte, würde das Objekt in den Linearisierungsregeln "a" außer Kraft setzen. Wenn ich "Merkmal T {def a: Int = 33}" hätte, wäre ok in diesem Fall 'override final val 'nicht möglich. Aber ich denke, das disqualifiziert immer noch nicht den Ansatz, standardmäßig _non-overriding_vals endgültig zu machen. –

+0

Einverstanden. Ich bin relativ neu in Scala ... habe versucht, mögliche Wege zu erkunden, an die du nicht gedacht hast. –

Antwort

20

Dies wird ausdrücklich in the specification angesprochen, und sie sind automatisch endgültig:

Mitglieder finaler Klassen oder Objekte sind implizit auch endgültig, so der final Modifikator für sie im Allgemeinen redundant ist, auch. Beachten Sie jedoch, dass Konstantwertdefinitionen (§4.1) einen expliziten Modifikator final erfordern, auch wenn sie in einer endgültigen Klasse oder einem letzten Objekt definiert sind.

Ihr final -weniger Beispiel kompiliert ohne Fehler (oder Warnungen) mit 2,10-M7, würde ich so davon ausgehen, dass es ein Problem mit den @switch in früheren Versionen überprüft, und dass die Mitglieder in der Tat sind endgültig.


Update: Eigentlich ist das neugieriger als ich erwartet hatte: Wenn wir folgendes mit entweder 2.9.2 oder 2,10-M7 kompilieren:

object NonFinal { 
    val a = 0 
} 

object Final { 
    final val a = 0 
} 

javap einen Unterschied macht zeigen:

public final class NonFinal$ implements scala.ScalaObject { 
    public static final NonFinal$ MODULE$; 
    public static {}; 
    public int a(); 
} 

public final class Final$ implements scala.ScalaObject { 
    public static final Final$ MODULE$; 
    public static {}; 
    public final int a(); 
} 

Sie sehen dasselbe, auch wenn die rechte Seite der Wertdefinitionen kein konstanter Ausdruck ist.

Also werde ich meine Antwort verlassen, aber es ist nicht schlüssig.

+2

Ich dachte, du könntest nur konstante Werte einschalten, in diesem Fall hat 2.10-M7 die Anforderung für "final", die du zitiert hast, entfernt. Nein? –

+0

@RexKerr: In der Tat. Ich kann mich nicht erinnern, das in einem der Änderungsprotokolle gesehen zu haben, aber ich werde es noch einmal überprüfen. –

+1

Danke, dieses Spezifikationszitat ist ziemlich klar, sagend, Val 's benötigen ausdrücklich' final'. Die 2.10.0-M7 ist dann seltsam, könnte es sich lohnen, den resultierenden Muster-Übereinstimmungscode zu überprüfen (vielleicht wird er "@ switch" stillgelegt?) –

1

Um die zentrale Frage nach endgültig auf einem Objekt zu richten, ich denke, diese Klausel von der Spezifikation relevanter ist:

Eine konstante Wert Definition ist von der Form final val x = e wobei e a konstanter Ausdruck (§6.24). Der letzte Modifikator muss vorhanden sein, und es darf keine Typannotation 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.

Von Bedeutung:

  • Keine Typenannotation
  • Der Ausdruck e gegeben werden kann, in dem erzeugten Code verwendet wird (durch meine Lektüre, wie das Original unevaluierten konstanten Ausdruck)

Es klingt für mich wie der Compiler von der Spezifikation erforderlich ist, diese eher wie Makroersetzungen anstelle von Werten zu verwenden, die zur Kompilierzeit an Ort und Stelle ausgewertet werden, was sich auf die resultierende co auswirken könnte de läuft.

Ich denke, es ist besonders interessant, dass keine Art Annotation gegeben werden kann.

Dies, denke ich, verweist auf unsere ultimative Antwort, obwohl ich kein Beispiel finden kann, das den Laufzeitunterschied für diese Anforderungen zeigt. Tatsächlich bekomme ich in meinem 2.9.2-Interpreter nicht einmal die Durchsetzung der ersten Regel.

20

Sie fragen nicht "warum sind sie nicht endgültig", Sie fragen "warum sind sie nicht inline." Es kommt einfach vor, dass Sie den Compiler, den Sie inline wollen, in den letzten Schritt bringen.

Der Grund, warum sie nicht automatisch inline sind, ist eine separate Kompilierung.

object A { final val x = 55 } 
object B { def f = A.x } 

Wenn Sie diese kompilieren, gibt B.f 55, wörtlich:

public int f(); 
    0: bipush  55 
    2: ireturn  

Das bedeutet, wenn Sie ein neu kompilieren, B auf die Änderung nicht bewusst sein. Wenn x nicht endgültig in A markiert, sieht B.f wie diese statt:

0: getstatic  #19     // Field A$.MODULE$:LA$; 
    3: invokevirtual #22     // Method A$.x:()I 
    6: ireturn  

, auch eine der anderen Antworten zu korrigieren, final bedeutet nicht unveränderlich in scala.

+1

final bedeutet auch nicht konstant. Versuchen Sie "final var x = 1; x = 2". – extempore

+0

sieht aus wie dies sollte die akzeptierte Antwort sein – fredoverflow