Scala的新功能,无论我阅读了多少篇文章/教程(如these | three | ones),我似乎无法围绕构造函数的工作方式。
我们来看一个主要构造函数的三个例子:
// 1
class Fizz(buzz : Buzz) { ... }
// 2
class Fizz (val buzz : Buzz) { ... }
// 3
class Fizz (var buzz : Buzz) { ... }
对于以下每一个:
buzz
属性?
buzz
字段?
答案 0 :(得分:4)
构造函数实际上是Scala中非常野蛮的野兽。事实上,Scala编译器能够为您做出一些明智的选择。
通过你的问题的措辞,我认为你可能有一些Java经验。为了清楚地说明发生了什么 - 并允许您在将来进行实验 - 让我们反编译Scala编译器生成的代码,以便我们可以看到Java等价物。为简明起见,我只会显示方法/字段的声明,而不是它们的实现。
buzz
仅在构造函数class Fizz(buzz : Buzz)
将编译为Java等效的
public class Fizz {
public Fizz(Buzz);
}
由于Fizz
没有声明引用buzz
的任何方法/字段(除了构造函数本身),Scala不会为它创建任何字段/方法。
buzz
在构造函数class Fizz(buzz : Buzz) {
def foo: Buzz = buzz
}
此buzz
方法使用foo
,因此编译器必须将其存储为字段。由于该字段未声明为var
,因此会将其设为private final
。
public class Fizz {
private final Buzz buzz;
public Buzz foo();
public Fizz(Buzz);
}
val
限定符class Fizz(val buzz : Buzz)
这一次,您明确表示您希望buzz
成为val
。这样可以创建一个private final
字段来保存buzz
,以及一个公共方法来访问它。
public class Fizz {
private final Buzz buzz;
public Buzz buzz();
public Fizz(Buzz);
}
var
限定符class Fizz(var buzz : Buzz)
这种情况与前一种情况非常相似,只是现在您要指定您希望能够修改buzz
。这将导致Scala编译器为您提供一个setter方法,名称为buzz_$eq
。由于命名方法中存在JVM约束,因此只需要$
shenanigan。在Scala代码中,此方法将显示为buzz_=
,语法糖将允许您将其称为fizz.buzz = someBuzz
。这样,它实际上看起来好像你在变异为字段,但你实际上只是在调用一个setter。
public class Fizz {
private Buzz buzz;
public Buzz buzz();
public void buzz_$eq(Buzz);
public Fizz(Buzz);
}
在调查此类问题时,这些命令很有用:
scalac Fizz.scala
将创建一个不可读的编译Fizz.class
。您可以使用
javap -constants -p Fizz
在包含Fizz.class
答案 1 :(得分:0)
在所有情况下,您显示的字段也是构造函数的参数。
声明为val或var的参数成为公共成员。如果你在构造函数中使用变量,它们将不会成为成员,如果你在类中使用它们,它们将是私有成员。
在第一种情况class Fizz(buzz : Buzz){}
中buzz
参数是不可变的而不是成员(我假设您不在任何地方使用它)。
在第二种情况class Fizz (val buzz : Buzz) {}
中,buzz
参数是不可变的,并成为公共成员。
在第三种情况class Fizz (var buzz : Buzz) {}
中,buzz
参数是可变的并成为公共成员。
在所有情况下,并没有自动创建任何getter或setter。