51

有什么区别:

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
}

我可以将参数声明为vars,然后再更改它们的值吗?例如,

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

  age = happyBirthday(5)

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

6 回答 6

51

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

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 的值。

于 2013-03-26T14:08:58.537 回答
10

这个

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

使类的用户可以在外部使用这些字段,例如您可以稍后执行

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

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

于 2013-03-26T14:08:32.783 回答
9

如果你熟悉 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,则该类将具有同名的成员变量。

于 2014-11-23T22:39:33.403 回答
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 声明,因此名称和年龄只能在构造函数中使用。

>>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);
}

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

>>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 =

于 2017-10-16T15:54:20.393 回答
0

The answer by @Reza where the author explores byte code using javap helped me to clarify this concept the best. To cite a very specific example of this case please refer to below scenario that I faced in my production web app (Play + Scala): How to inject parameters into a class/trait method in Scala

If I don't use val prefix to injected parameter authorizationHandler then compiler throws this error:

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

Sadly the error didn't help me to pinpoint to the correct issue which is to prefix with val.

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

   def myAction = AuthenticatedAction { implicit request =>
     ...
   }
} 
于 2018-03-02T07:12:40.273 回答
0

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

于 2016-04-12T19:02:07.677 回答