Scala 主构造函数及其生成的字段/属性

sme*_*eeb 2 constructor scala

Scala 新手,无论我读了多少文章/教程(比如这些|三个|),我似乎都无法理解构造函数是如何工作的。

让我们在这里举三个主构造函数的例子:

// 1
class Fizz(buzz : Buzz) { ... }

// 2
class Fizz (val buzz : Buzz) { ... }

// 3
class Fizz (var buzz : Buzz) { ... }
Run Code Online (Sandbox Code Playgroud)

对于其中每一个:

  • 是否buzz创建了财产?
    • 如果有的话,是公开的吗?
    • 它是可变的吗?
    • 是静态的吗?
    • 是否为其创建了 getter/setter?
  • 是否buzz创建了字段?
    • 如果有的话,是公开的吗?
    • 它是可变的吗?
    • 是静态的吗?
    • 是否为其创建了 getter/setter?
  • 还有什么特别/值得注意的吗?

fra*_*isr 5

构造函数实际上是 Scala 中的一头野兽。事实上,Scala 编译器能够为您做出一些明智的选择。

从你的问题的措辞来看,我认为你可能对 Java 有一定的经验。为了完全清楚发生了什么 - 并允许您将来进行实验 - 让我们反编译 Scala 编译器生成的代码,以便我们可以看到 Java 的等效代码。为了简洁起见,我将仅显示方法/字段的声明,而不显示它们的实现。

无限定符,buzz仅在构造函数中使用

class Fizz(buzz : Buzz)
Run Code Online (Sandbox Code Playgroud)

将编译为 Java 等效项

public class Fizz {
    public Fizz(Buzz);
}
Run Code Online (Sandbox Code Playgroud)

由于Fizz没有声明任何方法/字段引用buzz(除了构造函数本身),Scala 不会为其创建任何字段/方法。

无限定符,buzz在构造函数外部引用

class Fizz(buzz : Buzz) {
    def foo: Buzz = buzz
}
Run Code Online (Sandbox Code Playgroud)

这次buzz由方法使用foo,因此编译器必须将其存储为字段。由于该字段未声明为 a var,因此将进行创建private final

public class Fizz {
  private final Buzz buzz;
  public Buzz foo();
  public Fizz(Buzz);
}
Run Code Online (Sandbox Code Playgroud)

val预选赛

class Fizz(val buzz : Buzz)
Run Code Online (Sandbox Code Playgroud)

这一次,你明确表示你想buzz成为一名valprivate final这将产生创建一个用于保存的字段buzz以及一个用于访问它的公共方法的效果。

public class Fizz {
  private final Buzz buzz;
  public Buzz buzz();
  public Fizz(Buzz);
}
Run Code Online (Sandbox Code Playgroud)

var预选赛

class Fizz(var buzz : Buzz)
Run Code Online (Sandbox Code Playgroud)

这种情况与前一种情况非常相似,只不过现在您指定您希望能够修改buzz. 这将导致 Scala 编译器为您提供一个 setter 方法,其有趣的名称为buzz_$eq. 之所以需要这个$恶作剧,只是因为 JVM 在命名方法方面有限制。在您的 Scala 代码中,此方法将显示为buzz_=,语法糖将允许您将其称为fizz.buzz = someBuzz。这样,它实际上看起来好像您正在作为字段进行变异,但实际上您只是调用了 setter。

public class Fizz {
  private Buzz buzz;
  public Buzz buzz();
  public void buzz_$eq(Buzz);
  public Fizz(Buzz);
}
Run Code Online (Sandbox Code Playgroud)

如何反编译Scala?

这些命令在调查此类问题时很有帮助:

scalac Fizz.scala
Run Code Online (Sandbox Code Playgroud)

将创建一个Fizz.class不可读的编译版本。您可以使用以下命令将其反编译为 Java 版本

javap -constants -p Fizz
Run Code Online (Sandbox Code Playgroud)

在包含的目录中Fizz.class