¿Por qué `private val` y` private final val` son diferentes?

Solía ​​pensar que private val y private val private final val son los mismos, hasta que vi la sección 4.1 en Scala Reference:

Una definición de valor constante es de 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 a sí mismas como expresiones constantes; en el código generado son reemplazados 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 } 

salida javap -c :

 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."":()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 val no es private final val .

¿Por qué Scalac no solo trata el valor private val como private final val ? ¿Hay alguna razón subyacente?

Por lo tanto, esto es sólo una suposición, pero fue una molestia perenne en Java que las variables estáticas finales con un literal en el lado derecho se introduzcan en bytecode como constantes. Eso engendra un beneficio de rendimiento seguro, pero hace que la compatibilidad binaria de la definición se rompa si la “constante” cambia alguna vez. Al definir una variable estática final cuyo valor podría necesitar cambiar, los progtwigdores de Java tienen que recurrir a hacks como inicializar el valor con un método o constructor.

Un valor en Scala ya es definitivo en el sentido de Java. Parece que los diseñadores de Scala están utilizando el modificador redundante final para significar “permiso para alinear el valor constante”. Por lo tanto, los progtwigdores de Scala tienen control total sobre este comportamiento sin recurrir a los hacks: si quieren una constante en línea, un valor que nunca debería cambiar pero es rápido, escriben “val final”. si quieren flexibilidad para cambiar el valor sin romper la compatibilidad binaria, solo “val”.

Creo que la confusión aquí surge al combinar la inmutabilidad con la semántica de la final. val s se puede anular en clases secundarias y, por lo tanto, no se puede tratar como final a menos que se marque como tal explícitamente.

@Brian El REPL proporciona scope de clase en el nivel de línea. Ver:

 scala> $iw.getClass.getPackage res0: Package = package $line3 scala> private val x = 5 :5: error: value x cannot be accessed in object $iw lazy val $result = `x` scala> private val x = 5; println(x); 5