Comportamiento de inconsistencia de Scala para valores finales con y sin adscripción de tipo
Frecuentes
Visto 167 equipos
5
Estoy utilizando scala 2.10.3
and I noticed the following behavior
object TestConstantScala {
final val str1 : String = "foo:" + number1
final val number1 : Int = 123
final val number2 : Int = 123
final val str2 : String = "foo:" + number2
def main(args: Array[String]){
System.out.println(str1)
System.out.println(str2)
}
}
salida:
foo:0
foo:123
And my question is why the order makes a difference. in addition if I omit the Int
definition it returns to behave as normal
2 Respuestas
6
Without the type ascription (: Int
) number1
doesn't even exist as a field, and therefore doesn't need to be initialized. Instead, the compiler creates an accessor method that returns the value 123
directly, and in the constructor uses the literal value 123
para inicializar str1
.
Why does it create a field when there is a type ascription? It really makes no sense in this case, but sometimes type ascription can require code that does transformations of a value, like boxing a primitive or applying an implicit conversion. These operations should only be done once, both for semantic reasons (object identity, side-effects in the implicit conversion) and for efficiency. Therefore the result must be stored in a field.
So the behavior without a type ascription is an optimization for final
primitive fields initialized to a constant value, and the compiler isn't smart enough to apply the optimization when a type ascription is present.
Here is a more minimal example:
object TestConstantScala {
final val brokenStr: String = "foo:" + brokenNumber
final val brokenNumber: Int = 123
final val workingStr: String = "foo:" + workingNumber
final val workingNumber = 123
println(brokenStr)
println(workingStr)
}
Y aquí está la salida de scalac -Xprint:constructors
, showing the AST right after moving initialization into the constructor:
[[syntax trees at end of constructors]] // test18.scala
package <empty> {
object TestConstantScala extends Object {
final private[this] val brokenStr: String = _;
final <stable> <accessor> def brokenStr(): String = TestConstantScala.this.brokenStr;
final private[this] val brokenNumber: Int = _;
final <stable> <accessor> def brokenNumber(): Int = TestConstantScala.this.brokenNumber;
final private[this] val workingStr: String = _;
final <stable> <accessor> def workingStr(): String = TestConstantScala.this.workingStr;
final <stable> <accessor> def workingNumber(): Int(123) = 123;
def <init>(): TestConstantScala.type = {
TestConstantScala.super.<init>();
TestConstantScala.this.brokenStr = "foo:".+(scala.Int.box(TestConstantScala.this.brokenNumber()));
TestConstantScala.this.brokenNumber = 123;
TestConstantScala.this.workingStr = "foo:".+(scala.Int.box(123));
scala.this.Predef.println(TestConstantScala.this.brokenStr());
scala.this.Predef.println(TestConstantScala.this.workingStr());
()
}
}
}
Notice how there is no field for workingNumber
, only an accessor, and how in the constructor workingStr
se inicializa con "foo:".+(scala.Int.box(123))
.
contestado el 28 de mayo de 14 a las 15:05
0
Hay dos cuestiones en juego:
- initalization order (as @wingedsubmariner correctly mentioned)
- constant value definitions (and no an optimization). This is how you can get efficient pattern matches when using symbolic values, instead of literals (and Java overloads the
final
keyword the same way).
From the Scala Language Specification, Section 4.1 Value Declarations and Definitions
A constant value definition is of the form
final val x = e
where e is a constant expression (§6.24). The final modifier must be present and no type annotation may be given. References to the constant value x are themselves treated as constant expressions; in the generated code they are replaced by the definition’s right-hand side e.
contestado el 29 de mayo de 14 a las 00:05
No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas scala or haz tu propia pregunta.
As for the order -- it's the same behaviour as in java -- first, values are initialized to their default values (0 for int, null for reference types and so on), so at the time of initialization of
str1
initialized to 0, not to 123 (yet). What really interesting is why this behaviour is different with and without type ascriptions. - om-nom-nomposible duplicado de Why are `private val` and `private final val` different? - johanandren