Abi*_*oso 19 scala intellij-idea
我在Scala编写代码并在Intellij中做了一些快速重构,当时我偶然发现了下面的一些奇怪...
package misc
/**
* Created by abimbola on 05/10/15.
*/
object WTF extends App {
val name: String = name
println(s"Value is: $name")
}
Run Code Online (Sandbox Code Playgroud)
然后我注意到编译器没有抱怨,所以我决定尝试运行它,我得到了一个非常有趣的输出
Value is: null
Process finished with exit code 0
Run Code Online (Sandbox Code Playgroud)
谁能告诉我为什么这样有效?
编辑:
第一个问题,值名称被赋予对自身的引用,即使它尚不存在; 为什么恰好没有Scala编译器不会有错误的爆炸???
为什么赋值的值为null?
And*_*ann 14
这是一个简化的例子.这是编译因为通过给定类型可以推断出默认值:
class Example { val x: Int = x }
scalac Example.scala
Example.scala:1: warning: value x in class Example does nothing other than call itself recursively
class Example { val x: Int = x }
Run Code Online (Sandbox Code Playgroud)
这不会编译,因为不能推断出默认值:
class ExampleDoesNotCompile { def x = x }
scalac ExampleDoesNotCompile.scala
ExampleDoesNotCompile.scala:1: error: recursive method x needs result type
class ExampleDoesNotCompile { def x = x }
Run Code Online (Sandbox Code Playgroud)
我的解释.所以要注意:统一访问原则启动.val x
调用赋值器的赋值x()
返回x的单位化值.因此x设置为默认值.
class Example { val x: Int = x }
^
[[syntax trees at end of cleanup]] // Example.scala
package <empty> {
class Example extends Object {
private[this] val x: Int = _;
<stable> <accessor> def x(): Int = Example.this.x;
def <init>(): Example = {
Example.super.<init>();
Example.this.x = Example.this.x();
()
}
}
} ^
Run Code Online (Sandbox Code Playgroud)
默认值由Scala编译到的环境确定.
在您给出的示例中,您看起来就像在JVM上运行一样.这里Object的默认值是null
.
因此,当您不提供值时,默认值将用作回退.
默认值JVM:
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char '\u0000'
boolean false
Object null // String are objects.
Run Code Online (Sandbox Code Playgroud)
此外,默认值是给定类型的有效值:以下是REPL中的示例:
scala> val x : Int = 0
x: Int = 0
scala> val x : Int = null
<console>:10: error: an expression of type Null is ineligible for implicit conversion
val x : Int = null
^
scala> val x : String = null
x: String = null
Run Code Online (Sandbox Code Playgroud)
kir*_*uku 11
为什么Scala编译器没有错误爆炸?
因为这个问题在一般情况下无法解决.你知道停止问题吗?暂停问题说,不可能编写一个算法来查明程序是否会停止.由于发现递归定义是否会导致空分配的问题可以减少到暂停问题,因此也无法解决它.
好吧,现在很容易禁止递归定义,例如,对于没有类值的值,这样做:
scala> def f = { val k: String = k+"abc" }
<console>:11: error: forward reference extends over definition of value k
def f = { val k: String = k+"abc" }
^
Run Code Online (Sandbox Code Playgroud)
对于类值,出于以下几个原因,不禁止使用此功能:
您的用例很简单,就像这样:
scala> val k: String = k+"abc"
k: String = nullabc
Run Code Online (Sandbox Code Playgroud)
但是这个怎么样:
scala> object X { val x: Int = Y.y+1 }; object Y { val y: Int = X.x+1 }
defined object X
defined object Y
scala> X.x
res2: Int = 2
scala> Y.y
res3: Int = 1
scala> object X { val x: Int = Y.y+1 }; object Y { val y: Int = X.x+1 }
defined object X
defined object Y
scala> Y.y
res4: Int = 2
scala> X.x
res5: Int = 1
Run Code Online (Sandbox Code Playgroud)
或这个:
scala> val f: Stream[BigInt] = 1 #:: 1 #:: f.zip(f.tail).map { case (a,b) => a+b }
f: Stream[BigInt] = Stream(1, ?)
scala> f.take(10).toList
res7: List[BigInt] = List(1, 1, 2, 3, 5, 8, 13, 21, 34, 55)
Run Code Online (Sandbox Code Playgroud)
正如您所看到的那样,编写程序非常容易,因为程序不再明显,它们将产生哪些价值.由于暂停问题无法解决,我们不能让编译器在非平凡的情况下为我们工作.
这也意味着,在您的问题中显示的那些简单的案例可以在编译器中进行硬编码.但由于不存在可以检测所有可能的微不足道案例的算法,所以发现的所有案例都需要在编译器中进行硬编码(更不用说不存在简单案例的定义).因此,即使开始对其中一些案例进行硬编码也是不明智的.最终会导致编译器速度变慢,编译器难以维护.
有人可能会争辩说,对于烧毁每一个用户的用例,至少硬编码这样一种极端情况是明智的.另一方面,有些人只需要被焚烧以便学习新东西.;)
我认为@Andreas的答案已经有了必要的信息.我将尝试提供其他说明:
当你val name: String = name
在班级写作时,这会同时做一些不同的事情:
name
name()
name = name
,它成为主构造函数的一部分这就是安德烈亚斯1.1所说的
package <empty> {
class Example extends Object {
private[this] val x: Int = _;
<stable> <accessor> def x(): Int = Example.this.x;
def <init>(): Example = {
Example.super.<init>();
Example.this.x = Example.this.x();
()
}
}
}
Run Code Online (Sandbox Code Playgroud)
语法不是Scala,它(如建议的那样[[syntax trees at end of cleanup]]
)是编译器稍后将转换为字节码的文本表示.除了一些不熟悉的语法之外,我们可以解释这个,就像JVM一样:
val x: Int = _;
就像int x;
在Java中,即使用JVM的缺省值,它是0
用于I
(即int
Java或Int
Scala中)x()
x()
返回x
,这是0
x
被安排了 0
正如您所看到的,在初始解析步骤之后,语法树中没有任何内容似乎立即出错,即使原始源代码看起来不对.我不会说这是我期望的行为,所以我想象三件事之一:
(排序反映了我对降序的可能性的看法)
归档时间: |
|
查看次数: |
1062 次 |
最近记录: |