假设这个Java代码:
public class A {
public A(String g) {
x += g.length();
}
private int x = 0;
}
如果我创建A的实例,就像这样:
A a = new A("geo");
在此调用之后,x的值将为3.我在Scala代码中出错了什么?
class A(val g:String) {
x += g.length
var x:Int = 0
}
object x extends Application {
val x = new A("geo")
println(x.x)
}
这打印0.我假设当编译器到达var x:Int = 0
时,主构造函数的主体已经结束。我错了吗?你怎么能在Scala中声明实例变量(假设我不想在构造函数中使用它们)?
答案 0 :(得分:7)
请记住,您的代码会转换为类似的内容(但不完全相反):
public class A {
private final String g;
private int x;
public A(String g) {
this.g = g;
x_$eq(x() + g.length());
x = 0;
}
public void x_$eq(int arg0) {
x = arg0;
}
public int x() {
return x;
}
public String g() {
return g;
}
}
但是(非构造函数)方法中定义的变量会被转换为实际的局部变量。
不确定这是否能解释其中的原因之一。
编辑 - 为了清晰起见,将scala中的“翻译”更改为java,以及更准确地表示正在发生的事情的能力。
答案 1 :(得分:7)
您对Scala中构造函数的工作原理存在误解,导致您的困惑。具体来说,让我们将您发布的Scala代码翻译成Java:
class A(val g:String) {
x += g.length
var x:Int = 0
}
变为
public class A {
public A(String g) {
x += g.length();
x = 0;
}
private int x;
}
原因很简单。 Scala中类的整个主体是该类的主要构造函数。这意味着其中的语句以及初始化val
和var
语句将按照找到的顺序执行。
PS:这是该代码的实际真实再现。
Scala 2.7
C:\Users\Daniel\Documents\Scala\Programas> scalac -print A.scala
[[syntax trees at end of cleanup]]// Scala source: A.scala
package <empty> {
class A extends java.lang.Object with ScalaObject {
@remote def $tag(): Int = scala.ScalaObject$class.$tag(A.this);
<paramaccessor> private[this] val g: java.lang.String = _;
<stable> <accessor> <paramaccessor> def g(): java.lang.String = A.this.g;
private[this] var x: Int = _;
<accessor> def x(): Int = A.this.x;
<accessor> def x_=(x$1: Int): Unit = A.this.x = x$1;
def this(g: java.lang.String): A = {
A.this.g = g;
A.super.this();
A.this.x_=(A.this.x().+(g.length()));
A.this.x = 0;
()
}
}
}
Scala 2.8
C:\Users\Daniel\Documents\Scala\Programas>scalac -print A.scala
[[syntax trees at end of cleanup]]// Scala source: A.scala
package <empty> {
class A extends java.lang.Object with ScalaObject {
<paramaccessor> private[this] val g: java.lang.String = _;
<stable> <accessor> <paramaccessor> def g(): java.lang.String = A.this.g;
private[this] var x: Int = _;
<accessor> def x(): Int = A.this.x;
<accessor> def x_=(x$1: Int): Unit = A.this.x = x$1;
def this(g: java.lang.String): A = {
A.this.g = g;
A.super.this();
A.this.x_=(A.this.x().+(g.length()));
A.this.x = 0;
()
}
}
}
答案 2 :(得分:4)
改变这个:
class A(val g:String) {
x += g.length
var x:Int = 0
}
到
class A(val g:String) {
var x = g.length
}
答案 3 :(得分:4)
var x:Int = 0 将此作为构造函数的第一行
class A(val g:String) {
var x:Int = 0
x += g.length
}
答案 4 :(得分:0)
为什么Scala允许您在声明之前引用x
?在任何其他范围内,这都是非法的。
scala> def foo(g:String) = { x+=1; var x=0; x}
<console>:4: error: forward reference extends over definition of variable x
def foo(g:String) = { x+=1; var x=0; x}