编译器错误-奇怪的scala编译没有失败,但给出了错误的答案



我有下面的scala代码。我把CLASS_TYPE放在MAX_DATA_PAGE_SIZE定义后面,犯了一个错误。我以为这个不能编译,或者它可以给出正确的答案,但它输出0,为什么会发生这种情况?

object hello extends App{
    val baseSize = 256
    val MIN_DATA_PAGE_SIZE = math.max(baseSize, 1024)
    val MAX_DATA_PAGE_SIZE =  MIN_DATA_PAGE_SIZE * scala.math.pow(2, CLASS_TYPE-1).toInt
    val CLASS_TYPE: Int = 2
    println(MAX_DATA_PAGE_SIZE)
}

你是对的,由于使用了未初始化的变量,这不会在C++这样的语言中编译。正如德米特里的回答所提到的,编译器确实警告过你。然而,scala被编译成Java,如下所示:

~$ scalac -print hello.scala
hello.scala:4: warning: Reference to uninitialized value CLASS_TYPE
    val MAX_DATA_PAGE_SIZE =  MIN_DATA_PAGE_SIZE * scala.math.pow(2, CLASS_TYPE-1).toInt
                                                                     ^
[[syntax trees at end of                   cleanup]] // hello.scala
package <empty> {
  object hello extends Object with App {
    <stable> <accessor> def executionStart(): Long = hello.this.executionStart;
    @deprecatedOverriding("executionStart should not be overridden", "2.11.0") private[this] val executionStart: Long = _;
    final <accessor> def _args(): Array[String] = hello.this._args;
    private[this] var _args: Array[String] = _;
    final <accessor> def _args_=(x$1: Array[String]): Unit = {
      hello.this._args = x$1;
      ()
    };
    final <stable> <accessor> def initCode(): scala.collection.mutable.ListBuffer = hello.this.initCode;
    private[this] val initCode: scala.collection.mutable.ListBuffer = _;
    <accessor> def scala$App$_setter_$executionStart_=(x$1: Long): Unit = {
      hello.this.executionStart = x$1;
      ()
    };
    <accessor> def initCode_=(x$1: scala.collection.mutable.ListBuffer): Unit = {
      hello.this.initCode = x$1;
      ()
    };
    @deprecatedOverriding("args should not be overridden", "2.11.0") def args(): Array[String] = scala.App$class.args(hello.this);
    @deprecated("The delayedInit mechanism will disappear.", "2.11.0") override def delayedInit(body: Function0): Unit = scala.App$class.delayedInit(hello.this, body);
    @deprecatedOverriding("main should not be overridden", "2.11.0") def main(args: Array[String]): Unit = scala.App$class.main(hello.this, args);
    private[this] val baseSize: Int = _;
    <stable> <accessor> def baseSize(): Int = hello.this.baseSize;
    private[this] val MIN_DATA_PAGE_SIZE: Int = _;
    <stable> <accessor> def MIN_DATA_PAGE_SIZE(): Int = hello.this.MIN_DATA_PAGE_SIZE;
    private[this] val MAX_DATA_PAGE_SIZE: Int = _;
    <stable> <accessor> def MAX_DATA_PAGE_SIZE(): Int = hello.this.MAX_DATA_PAGE_SIZE;
    private[this] val CLASS_TYPE: Int = _;
    <stable> <accessor> def CLASS_TYPE(): Int = hello.this.CLASS_TYPE;
    final <synthetic> def delayedEndpoint$hello$1: Unit = {
      hello.this.baseSize = 256;
      hello.this.MIN_DATA_PAGE_SIZE = scala.math.`package`.max(hello.this.baseSize(), 1024);
      hello.this.MAX_DATA_PAGE_SIZE = hello.this.MIN_DATA_PAGE_SIZE().*(scala.math.`package`.pow(2.0, hello.this.CLASS_TYPE().-(1).toDouble()).toInt());
      hello.this.CLASS_TYPE = 2;
      scala.this.Predef.println(scala.Int.box(hello.this.MAX_DATA_PAGE_SIZE()));
      ()
    };
    def <init>(): hello.type = {
      hello.super.<init>();
      scala.App$class./*App$class*/$init$(hello.this);
      hello.this.delayedInit(new hello$delayedInit$body(hello.this));
      ()
    }
  };
  final <synthetic> class hello$delayedInit$body extends runtime.AbstractFunction0 {
    <paramaccessor> private[this] val $outer: hello.type = _;
    final def apply(): Object = {
      hello$delayedInit$body.this.$outer.delayedEndpoint$hello$1();
      scala.runtime.BoxedUnit.UNIT
    };
    def <init>($outer: hello.type): hello$delayedInit$body = {
      if ($outer.eq(null))
        throw null
      else
        hello$delayedInit$body.this.$outer = $outer;
      hello$delayedInit$body.super.<init>();
      ()
    }
  }
}
one warning found

这不会导致编译错误,但肯定会导致未定义的行为

如果将类加载到REPL中并运行两次,则会给出正确的答案,因为CLASS_TYPE已正确初始化。

scala> :load -v hello.scala
Loading hello.scala...
scala> object hello extends App{
     |     val baseSize = 256
     |     val MIN_DATA_PAGE_SIZE = math.max(baseSize, 1024)
     |     val MAX_DATA_PAGE_SIZE =  MIN_DATA_PAGE_SIZE * scala.math.pow(2, CLASS_TYPE-1).toInt
     |     val CLASS_TYPE: Int = 2
     | 
     |     println(MAX_DATA_PAGE_SIZE)
     | }
<console>:10: warning: Reference to uninitialized value CLASS_TYPE
           val MAX_DATA_PAGE_SIZE =  MIN_DATA_PAGE_SIZE * scala.math.pow(2, CLASS_TYPE-1).toInt
                                                                            ^
defined object hello
scala> hello.main(Array())
0
scala> hello.main(Array())
2048

这与编译扩展应用程序特性的对象的方式有关。

如果你写了一个像这样的普通应用程序:

object hello {
def main(args:Array[String]) {
    val baseSize = 256
    val MIN_DATA_PAGE_SIZE = math.max(baseSize, 1024)
    val MAX_DATA_PAGE_SIZE =  MIN_DATA_PAGE_SIZE * scala.math.pow(2, CLASS_TYPE-1).toInt
    val CLASS_TYPE: Int = 2
    println(MAX_DATA_PAGE_SIZE)
  }
}

并尝试编译它,你会得到以下错误:

~$ scalac hello.scala
hello.scala:6: error: forward reference extends over definition of value MAX_DATA_PAGE_SIZE
val MAX_DATA_PAGE_SIZE =  MIN_DATA_PAGE_SIZE * scala.math.pow(2, CLASS_TYPE-1).toInt
                                                                         ^
one error found

编译器在编译时向您发出警告,如

[warn] Reference to uninitialized value CLASS_TYPE
[warn]   val MAX_DATA_PAGE_SIZE =  MIN_DATA_PAGE_SIZE * scala.math.pow(2, CLASS_TYPE-1).toInt
[warn]                                                                    ^
[warn] one warning found

解决这个问题的最好方法是使用更严格的编译器选项。添加到您的建筑.sbt

scalacOptions ++= Seq(
"-unchecked",
"-deprecation",
"-feature",
"-Xfatal-warnings",
"-language:postfixOps",
"-Ywarn-unused-import"
)

还要注意,根据scala风格,你应该注意你的常量,比如

MaxDataPageSize
ClassType

等等。

最新更新