Perché `private val` e` private final val` sono diverse?


100

Lo pensavo private vale private final valsono la stessa cosa, finché non ho visto la sezione 4.1 in Scala Reference:

Una definizione di valore costante è della forma

final val x = e

dove e è un'espressione costante (§6.24). Il modificatore finale deve essere presente e non può essere fornita alcuna annotazione di tipo. I riferimenti al valore della costante x vengono trattati come espressioni costanti; nel codice generato sono sostituiti dal lato destro della definizione e.

E ho scritto un test:

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

javap -c produzione:

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
}

Il codice byte è proprio come dice Scala Reference: private valnon lo è private final val.

Perché lo scalac non tratta solo private valcome private final val? C'è una ragione di fondo?


28
In altre parole: poiché a valè già immutabile, perché abbiamo bisogno della finalparola chiave in Scala? Perché il compilatore non può trattare tutti vali messaggi allo stesso modo di final vals?
Jesper

Si noti che il privatemodificatore di ambito ha la stessa semantica package privatedi Java. Potresti voler dire private[this].
Connor Doyle

5
@ConnorDoyle: come pacchetto privato? Non credo: privatesignifica che è visibile solo alle istanze di questa classe, private[this]solo questa istanza - ad eccezione delle istanze della stessa classe , privatenon consente a nessuno (include dallo stesso pacchetto) di accedere al valore.
Make42

Risposte:


81

Quindi, questa è solo una supposizione, ma era una seccatura perenne in Java che le variabili statiche finali con un letterale sul lato destro venissero integrate nel bytecode come costanti. Ciò genera sicuramente un vantaggio in termini di prestazioni, ma causa l'interruzione della compatibilità binaria della definizione se la "costante" cambia. Quando si definisce una variabile statica finale il cui valore potrebbe dover cambiare, i programmatori Java devono ricorrere ad hack come l'inizializzazione del valore con un metodo o un costruttore.

Un val in Scala è già definitivo in senso Java. Sembra che i progettisti di Scala stiano usando il modificatore ridondante final per indicare "il permesso di incorporare il valore costante". Quindi i programmatori Scala hanno il controllo completo su questo comportamento senza ricorrere ad hack: se vogliono una costante inline, un valore che non dovrebbe mai cambiare ma è veloce, scrivono "final val". se vogliono flessibilità per modificare il valore senza interrompere la compatibilità binaria, basta "val".


9
Sì, questo è il motivo per i val non privati, ma ovviamente i val privati ​​non possono essere inseriti in altre classi e rompere la compatibilità allo stesso modo.
Alexey Romanov

3
C'è qualche problema di compatibilità binaria quando cambio private vala private final val?
Yang Bo

1
@ steve-waldman Scusi, intendevi valnel secondo paragrafo?
Yang Bo

1
Ecco i dettagli sulle variabili statiche finali in Java per quanto riguarda la compatibilità binaria: docs.oracle.com/javase/specs/jls/se7/html/…
Eran Medan

8

Penso che la confusione qui nasca dalla fusione dell'immutabilità con la semantica del finale. vals possono essere sovrascritti nelle classi figlie e quindi non possono essere trattati come final a meno che non siano contrassegnati come tali esplicitamente.

@Brian Il REPL fornisce l'ambito della classe a livello di linea. Vedere:

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

1
Sto parlando private val. Può essere ignorato?
Yang Bo

No, i valori privati ​​non possono essere ignorati. Puoi ridefinire un'altra val privata con lo stesso nome in una sottoclasse, ma è una val completamente diversa che ha lo stesso nome. (Tutti i riferimenti a quello vecchio rimanderebbero ancora a quello vecchio.)
aij

1
Tuttavia, non sembra essere solo questo comportamento prioritario, dal momento che posso creare un val finale (o anche una var finale) nell'interprete senza essere affatto nel contesto di una classe.
nairbv
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.