web-dev-qa-db-fra.com

Pourquoi le «val privé» et le «val final privé» sont-ils différents?

Je pensais que private val et private final val sont les mêmes, jusqu'à ce que je voie la section 4.1 dans Scala Référence:

Une définition de valeur constante est de la forme

final val x = e

où e est une expression constante (§6.24). Le dernier modificateur doit être présent et aucune annotation de type ne peut être donnée. Les références à la valeur constante x sont elles-mêmes traitées comme des expressions constantes; dans le code généré, ils sont remplacés par le côté droit de la définition e.

Et j'ai écrit un test:

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

javap -c sortie:

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
}

Le code d'octet est exactement comme Scala Reference dit: private val n'est pas private final val.

Pourquoi ne pas scalac traiter simplement private val comme private final val? Y a-t-il une raison sous-jacente?

96
Yang Bo

Donc, ce n'est qu'une supposition, mais c'était un ennui permanent dans Java que les variables statiques finales avec un littéral sur le côté droit soient intégrées dans le bytecode en tant que constantes. Cela engendre un avantage de performance bien sûr, mais cela entraîne la rupture de la compatibilité binaire de la définition si la "constante" change. Lors de la définition d'une variable statique finale dont la valeur pourrait devoir changer, Java doivent recourir à des hacks comme initialiser la valeur avec une méthode ou un constructeur.

Un val dans Scala est déjà final dans le sens Java. Il semble que les concepteurs de Scala utilisent le modificateur redondant final pour signifier "l'autorisation d'inclure la valeur constante") ". Donc Scala ont un contrôle complet sur ce comportement sans recourir à des hacks: s'ils veulent une constante en ligne, une valeur qui ne devrait jamais changer mais qui est rapide, ils écrivent" final val ". If ils veulent de la flexibilité pour changer la valeur sans casser la compatibilité binaire, juste "val".

80
Steve Waldman

Je pense que la confusion provient de la confusion de l'immuabilité avec la sémantique de la finale. vals peut être surchargé dans les classes enfants et ne peut donc pas être traité comme final à moins d'être marqué comme tel explicitement.

@Brian Le REPL fournit la portée de classe au niveau de la ligne. Voir:

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
8
Connor Doyle