scala类构造函数参数

时间:2013-03-26 14:04:47

标签: scala constructor scope immutability

有什么区别:

class Person(name: String, age: Int) {
  def say = "My name is " + name + ", age " + age
}

class Person(val name: String, val age: Int) { 
  def say = "My name is " + name + ", age " + age
}

我可以将参数声明为var s,并稍后更改其值吗?例如,

class Person(var name: String, var age: Int) {

  age = happyBirthday(5)

  def happyBirthday(n: Int) {
    println("happy " + n + " birthday")
    n
  }
}

6 个答案:

答案 0 :(得分:45)

对于第一部分,答案是范围:

scala> class Person(name: String, age: Int) {
     |   def say = "My name is " + name + ", age " + age
     | }

scala> val x = new Person("Hitman", 40)

scala> x.name
<console>:10: error: value name is not a member of Person
              x.name

如果您使用valvar为参数添加前缀,则会从课堂外看到它们,否则它们将是私有的,您可以在上面的代码中看到。

是的,你可以改变var的值,就像通常一样。

答案 1 :(得分:10)

class Person(val name: String, val age: Int)

使该字段的用户可以在外部使用,例如你以后可以做

val p = new Person("Bob", 23)
val n = p.name

如果您将args指定为var,则范围与val相同,但字段是可变的。

答案 2 :(得分:7)

如果您熟悉Java,可以从这个示例中获得想法:

class Person(name: String, age: Int)

类似
class Person {
  public Person(String name, int age) {
  }
}

虽然

class Person(var name: String, var age: Int) // also we can use 'val'

类似于

class Person {
  String name;
  int age;

  public Person(String name, int age) {
     this.name = name;
     this.age = age;
  }
}

直觉是没有var / val,变量只能在构造函数中访问。如果添加了var / val,则该类将具有相同名称的成员变量。

答案 3 :(得分:2)

这里的答案非常好,但我正在探索字节代码来解决这个问题。在类上应用javap时,它会打印出传递的类的包,受保护和公共字段以及方法。我创建了一个Person.scala类,并使用以下代码填充它。

class Person(name: String, age: Int) {
  def say = "My name is " + name + ", age " + age
}

class PersonVal(val name: String, val age: Int) {
  def say = "My name is " + name + ", age " + age
}

class PersonVar(var name: String, var age: Int) {

  age = happyBirthday(5)

  def happyBirthday(n: Int) = {
    println("happy " + n + " birthday")
    n
  }
}

使用scalac Person.scala编译代码后,会生成三个名为Person.class, PersonVal.calass , PersonVar.cass的文件。通过为每个类文件运行javap,我们可以看到结构如何:

>>javap Person.class
Compiled from "Person.scala"
public class Person {
  public java.lang.String say();
  public Person(java.lang.String, int);
}

在这种情况下,它没有为Person创建任何类变量,因为它既没有val也没有val声明,因此name和age只能在构造函数中使用。

>>javap PersonVal.class
public class PersonVal {
  public java.lang.String name();
  public int age();
  public java.lang.String say();
  public PersonVal(java.lang.String, int);
}

在这种情况下,它有三个成员,两个用于输入构造函数,另一个用于我们在constructore中声明的成员。但是,我们没有输入构造函数的任何setter,因此我们无法更改值。

>>javap PersonVar.class
public class PersonVar {
  public java.lang.String name();
  public void name_$eq(java.lang.String);
  public int age();
  public void age_$eq(int);
  public int happyBirthday(int);
  public PersonVar(java.lang.String, int);
}

它与PersonVal示例相同,但我们可以使用这些variable_$eq方法更改此情况下的值。它不仅仅是variable =

的缩短版本

答案 4 :(得分:0)

您可以使用case class,在这种情况下,Person类会在类外部提供这些变量。 case class Person(name: String, age: Int)。然后,以下代码将按预期工作。 val z = new Person("John", 20); z.name //John

答案 5 :(得分:0)

@Reza的答案是作者使用javap探索字节代码,这帮助我最好地澄清了这个概念。举一个这个案例的一个非常具体的例子,请参考我在生产网络应用程序(Play + Scala)中遇到的以下场景: How to inject parameters into a class/trait method in Scala

如果我不使用val前缀来注入参数authorizationHandler那么 编译器抛出此错误:

class MyController needs to be abstract, since method authorizationHandler in trait AuthorizationCheck of type => controllers.authapi.AuthorizationHandler is not defined
[error] class MyController @Inject() (authorizationHandler: AuthorizationHandler) extends Controller with AuthorizationCheck {
[error]       ^
[error] one error found

可悲的是,错误并没有帮助我找出正确的问题,即以val作为前缀。

class MyController @Inject()(val authorizationHandler: AuthorizationHandler) extends Controller with AuthorizationCheck {

   def myAction = AuthenticatedAction { implicit request =>
     ...
   }
}