奇怪的scala编译没有失败但给出了错误的答案
odd scala compile do not failed but give the wrong answer
我有以下 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)
}
编译器在编译时给了你一个警告,比如
[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
解决此问题的最佳方法是使用更严格的编译器选项。添加到您的 build.sbt
scalacOptions ++= Seq(
"-unchecked",
"-deprecation",
"-feature",
"-Xfatal-warnings",
"-language:postfixOps",
"-Ywarn-unused-import"
)
另请注意,根据 scala style,您应该注意
等常量
MaxDataPageSize
ClassType
等等。
你是对的,由于使用了未初始化的变量,这不会用像 C++ 这样的语言编译。正如 Dmitry 的回答所提到的那样,编译器确实会警告您。但是,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: Array[String]): Unit = {
hello.this._args = x;
()
};
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: Long): Unit = {
hello.this.executionStart = x;
()
};
<accessor> def initCode_=(x: scala.collection.mutable.ListBuffer): Unit = {
hello.this.initCode = x;
()
};
@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: 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();
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
这不会导致编译错误,但肯定会未定义的行为。
如果你将 class 加载到 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
这与编译扩展 App 特征的对象的方式有关。
如果你写了一个像这样的普通应用程序:
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
我有以下 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)
}
编译器在编译时给了你一个警告,比如
[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
解决此问题的最佳方法是使用更严格的编译器选项。添加到您的 build.sbt
scalacOptions ++= Seq(
"-unchecked",
"-deprecation",
"-feature",
"-Xfatal-warnings",
"-language:postfixOps",
"-Ywarn-unused-import"
)
另请注意,根据 scala style,您应该注意
等常量MaxDataPageSize
ClassType
等等。
你是对的,由于使用了未初始化的变量,这不会用像 C++ 这样的语言编译。正如 Dmitry 的回答所提到的那样,编译器确实会警告您。但是,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: Array[String]): Unit = {
hello.this._args = x;
()
};
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: Long): Unit = {
hello.this.executionStart = x;
()
};
<accessor> def initCode_=(x: scala.collection.mutable.ListBuffer): Unit = {
hello.this.initCode = x;
()
};
@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: 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();
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
这不会导致编译错误,但肯定会未定义的行为。
如果你将 class 加载到 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
这与编译扩展 App 特征的对象的方式有关。
如果你写了一个像这样的普通应用程序:
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