Scala 默认构造函数';s的参数转换为字段?

Scala 默认构造函数';s的参数转换为字段?,scala,Scala,我在Scala REPL中运行简单的代码,它创建了两个类,每个类都有一个Int值x。以下是方法: scala> class C(x: Int){} defined class C scala> new C(100).x <console>:13: error: value x is not a member of C new C(100).x ^ scala> class D(val x: Int){} defi

我在Scala REPL中运行简单的代码,它创建了两个类,每个类都有一个
Int
x
。以下是方法:

scala> class C(x: Int){}
defined class C

scala> new C(100).x
<console>:13: error: value x is not a member of C
       new C(100).x
                  ^

scala> class D(val x: Int){}
defined class D

scala> new D(100).x
res1: Int = 100 
scala>C类(x:Int){
定义的C类
scala>新的C(100).x
:13:错误:值x不是C的成员
新C(100.x)
^
scala>class D(val x:Int){}
定义的D类
scala>新的D(100).x
res1:Int=100
我的理解是,对于类
C
而言,变量
x
将成为可变变量(
var
默认情况下),而对于类D而言,则是不可变变量。然而,我遇到了这个问题,
x
不是
C
的成员


这是怎么回事?

Scala类中的属性可以有以下修饰符:

  • val
    使属性不可变;它始终是公共的-这是有意义的,因为值不能更改
  • var
    使属性可变并公开
  • 没有修饰符使属性可变且私有
代码示例:

// no modifier
class A(x: Int) {
  def print() = {
    x += 1 // this i fine, it's mutable
    println(x)
  }
}

val a = new A(3)
// a.x - compile error, it's private


// var
class A(var x: Int) {
  def print() = {
    x += 1 // this is fine, it's mutable
    println(x)
  }
}

    val a = new A(3)
    a.x // you can do this since it's public (modifier var)

// val
class A(val x: Int) {
  def print() = {
    // x += 1 // can't do this, it's immutable
    println(x)
  }
}

val a = new A(3)
a.x // you can do this since it's public (modifier val)

有关构造函数和类的详细信息:

Scala类中的属性可以具有以下修饰符:

  • val
    使属性不可变;它始终是公共的-这是有意义的,因为值不能更改
  • var
    使属性可变并公开
  • 没有修饰符使属性可变且私有
代码示例:

// no modifier
class A(x: Int) {
  def print() = {
    x += 1 // this i fine, it's mutable
    println(x)
  }
}

val a = new A(3)
// a.x - compile error, it's private


// var
class A(var x: Int) {
  def print() = {
    x += 1 // this is fine, it's mutable
    println(x)
  }
}

    val a = new A(3)
    a.x // you can do this since it's public (modifier var)

// val
class A(val x: Int) {
  def print() = {
    // x += 1 // can't do this, it's immutable
    println(x)
  }
}

val a = new A(3)
a.x // you can do this since it's public (modifier val)

关于构造函数和类的更多信息:

要研究这个问题,我们可以进行反向工程,看看“编译器会做什么?”:)

为此,我们正在编译包含以下内容的类
C.scala
C类(x:Int){}
通过运行:

scalac C.scala

这将生成
C.class
。 现在,我们可以使用javap来查看编译器将生成什么

运行
javap-pc.class
将产生:

public class C {
  public C(int);
}
如果我们重复整个过程
D类(val x:Int){}
我们将作出以下让步:

public class D {
  private final int x;
  public int x();
  public D(int);
}
我们可以看到区别在于关键字
val
告诉类创建一个getter方法

回到您的假设,如果没有
val
关键字,类变量将被定义为可变的:这是错误的。为了证明我们可以更深入地了解这个问题。通过运行:

javap-p-vc.class

我们(在许多其他信息中)得到以下片段:

{
  public C(int);
    descriptor: (I)V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=2
         0: aload_0
         1: invokespecial #14                 // Method java/lang/Object."<init>":()V
         4: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LC;
            0       5     1     x   I
      LineNumberTable:
        line 4: 0
        line 2: 4
    MethodParameters:
      Name                           Flags
      x                              final
}
{
公共图书馆(国际);
描述符:(I)V
旗帜:ACC_PUBLIC
代码:
堆栈=1,局部变量=2,参数大小=2
0:aload_0
1:invokespecial#14//方法java/lang/Object。“:()V
4:返回
LocalVariableTable:
起始长度插槽名称签名
050本信用证;
0 5 1 x 1
LineNumberTable:
第4行:0
第2行:4
方法参数:
姓名标志
x决赛
}

您可以清楚地看到,类变量
x
仍然声明为
final
,因此是不可变的。

要研究这个问题,我们可以通过反向工程查看“编译器会做什么?”:)

为此,我们正在编译包含以下内容的类
C.scala
C类(x:Int){}
通过运行:

scalac C.scala

这将生成
C.class
。 现在,我们可以使用javap来查看编译器将生成什么

运行
javap-pc.class
将产生:

public class C {
  public C(int);
}
如果我们重复整个过程
D类(val x:Int){}
我们将作出以下让步:

public class D {
  private final int x;
  public int x();
  public D(int);
}
我们可以看到区别在于关键字
val
告诉类创建一个getter方法

回到您的假设,如果没有
val
关键字,类变量将被定义为可变的:这是错误的。为了证明我们可以更深入地了解这个问题。通过运行:

javap-p-vc.class

我们(在许多其他信息中)得到以下片段:

{
  public C(int);
    descriptor: (I)V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=2
         0: aload_0
         1: invokespecial #14                 // Method java/lang/Object."<init>":()V
         4: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LC;
            0       5     1     x   I
      LineNumberTable:
        line 4: 0
        line 2: 4
    MethodParameters:
      Name                           Flags
      x                              final
}
{
公共图书馆(国际);
描述符:(I)V
旗帜:ACC_PUBLIC
代码:
堆栈=1,局部变量=2,参数大小=2
0:aload_0
1:invokespecial#14//方法java/lang/Object。“:()V
4:返回
LocalVariableTable:
起始长度插槽名称签名
050本信用证;
0 5 1 x 1
LineNumberTable:
第4行:0
第2行:4
方法参数:
姓名标志
x决赛
}

您可以清楚地看到,类变量
x
仍然声明为
final
,因此是不可变的。

第一段中的所有3个声明都是错误的:
val
var
并不总是公共的,这只是默认值;如果没有修饰符,您可能有也可能没有字段(这取决于参数是否在任何方法中使用),但无论如何它都是不可变的;您可以使用
private
modified,但我认为答案仍然存在,在这种情况下,它们默认为公共的。不过我可以编辑答案。我愿意编辑建议。关于方法,我不确定我是否完全理解。当您使用类似
class A(val t:Int)
的内容,并且
t
将是类的属性/字段时,示例是什么?当您有
class A(i:Int)
时,class
A
没有
i
的字段
i
只是一个(不可变)构造函数参数。当您有
class A(i:Int){def foo=i}
时,class
A
i
提供了一个私有的不可变字段。我明白了,谢谢。请在这里阅读答案:。虽然我回答的重点是