带有和不带有类型归属的最终 val 的 Scala 不一致行为 [英] Scala inconsistence behavior for final vals with and without type ascription

查看:34
本文介绍了带有和不带有类型归属的最终 val 的 Scala 不一致行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 scala 2.10.3 并且我注意到以下行为

I am using 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)
    }


}

输出:

foo:0  
foo:123  

我的问题是为什么顺序会有所不同.此外,如果我省略 Int 定义,它会返回正常行为

And my question is why the order makes a difference. in addition if I omit the Int definition it returns to behave as normal

推荐答案

没有类型归属 (: Int) number1 甚至不作为字段存在,并且因此不需要初始化.相反,编译器创建了一个直接返回值 123 的访问器方法,并在构造函数中使用文字值 123 来初始化 str1.

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 to initialize 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.

因此,没有类型归属的行为是对初始化为常量值的 final 原始字段的优化,并且编译器不够聪明,无法在存在类型归属时应用优化.

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)
}

这里是 scalac -Xprint:constructors 的输出,在将初始化移动到构造函数后立即显示 AST:

And here is the output from 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());
      ()
    }
  }
}

注意如何没有 workingNumber 字段,只有一个访问器,以及如何在构造函数中用 "foo:".+(scala.Int.box(123)).

Notice how there is no field for workingNumber, only an accessor, and how in the constructor workingStr is initialized with "foo:".+(scala.Int.box(123)).

这篇关于带有和不带有类型归属的最终 val 的 Scala 不一致行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆