¿Por qué son diferentes el "valor privado" y el "valor final privado"?

100

Solía ​​pensar eso private valy private final valson lo mismo, hasta que vi la sección 4.1 en Scala Reference:

Una definición de valor constante tiene la forma

final val x = e

donde e es una expresión constante (§6.24). El modificador final debe estar presente y no se puede dar ninguna anotación de tipo. Las referencias al valor constante x se tratan en sí mismas como expresiones constantes; en el código generado se reemplazan por el lado derecho de la definición e.

Y he escrito una prueba:

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

javap -c salida:

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
}

El código de bytes es tal como lo dijo Scala Reference: private valno lo es private final val.

¿Por qué scalac no trata simplemente private valcomo private final val? ¿Existe alguna razón subyacente?

Yang Bo
fuente
28
En otras palabras: dado que a valya es inmutable, ¿por qué necesitamos la finalpalabra clave en Scala? ¿Por qué el compilador no puede tratar todos los correos electrónicos de valla misma manera que final vallos correos electrónicos?
Jesper
Tenga en cuenta que el privatemodificador de alcance tiene la misma semántica que package privateen Java. Puede querer decir private[this].
Connor Doyle
5
@ConnorDoyle: ¿Como paquete privado? No lo creo: privatesignifica que solo es visible para instancias de esta clase, private[this]solo esta instancia, excepto para instancias de la misma clase , privateno permite que nadie (incluido del mismo paquete) acceda al valor.
Make42

Respuestas:

81

Entonces, esto es solo una suposición, pero fue una molestia constante en Java que las variables estáticas finales con un literal en el lado derecho se incluyan en el código de bytes como constantes. Eso engendra un beneficio de rendimiento seguro, pero hace que la compatibilidad binaria de la definición se rompa si la "constante" alguna vez cambia. Al definir una variable estática final cuyo valor podría necesitar cambiar, los programadores de Java tienen que recurrir a trucos como inicializar el valor con un método o constructor.

Un val en Scala ya es final en el sentido de Java. Parece que los diseñadores de Scala están usando el modificador redundante final para significar "permiso para incorporar el valor constante". Así que los programadores de Scala tienen control total sobre este comportamiento sin recurrir a hacks: si quieren una constante en línea, un valor que nunca debería cambiar pero que es rápido, escriben "final val". si quieren flexibilidad para cambiar el valor sin romper la compatibilidad binaria, simplemente "val".

Steve Waldman
fuente
9
Sí, esa es la razón de los valores no privados, pero los valores privados obviamente no pueden insertarse en otras clases y romper la compatibilidad de la misma manera.
Alexey Romanov
3
¿Hay algún problema de compatibilidad binaria cuando cambio private vala private final val?
Yang Bo
1
@ steve-waldman Disculpe, ¿quiso decir valen su segundo párrafo?
Yang Bo
1
Aquí están los detalles sobre las variables estáticas finales en Java con respecto a la compatibilidad binaria: docs.oracle.com/javase/specs/jls/se7/html/…
Eran Medan
8

Creo que la confusión aquí surge al combinar la inmutabilidad con la semántica de final. vals se pueden anular en clases secundarias y, por lo tanto, no se pueden tratar como finales a menos que se marquen explícitamente como tales.

@Brian El REPL proporciona un alcance de clase a nivel de línea. Ver:

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
fuente
1
Estoy hablando private val. ¿Se puede anular?
Yang Bo
No, los valores privados no se pueden anular. Puede redefinir otro valor privado con el mismo nombre en una subclase, pero es un valor completamente diferente que simplemente tiene el mismo nombre. (Todas las referencias a la antigua se seguirían refiriendo a la antigua.)
aij
1
Sin embargo, no parece ser solo este comportamiento predominante, ya que puedo hacer un valor final (o incluso una var final) en el intérprete sin estar en el contexto de una clase en absoluto.
nairbv