在Scala中使用def,val和var


158
class Person(val name:String,var age:Int )
def person = new Person("Kumar",12)
person.age = 20
println(person.age)

这些代码行输出12,即使person.age=20已成功执行。我发现发生这种情况是因为我使用了def in def person = new Person("Kumar",12)。如果我使用var或val,则输出为20。我了解Scala中的默认值为val。这个:

def age = 30
age = 45

...给出编译错误,因为默认情况下为val。为什么上面的第一行行不能正常工作,但是也不会出错?

Answers:


254

在Scala中定义事物的方式有以下三种:

  • def定义方法
  • val定义一个固定(无法修改)
  • var定义一个变量(可以修改)

查看您的代码:

def person = new Person("Kumar",12)

这定义了一个称为的新方法person。您不能调用此方法,()因为它被定义为无参数方法。对于空方法,您可以使用“()”或不使用“()”来调用它。如果您只写:

person

那么您正在调用此方法(如果您不分配返回值,它将被丢弃)。在这行代码中:

person.age = 20

发生的情况是,您首先调用该person方法,然后在返回值(class的实例Person)上更改age成员变量。

最后一行:

println(person.age)

在这里,您再次调用该person方法,该方法返回一个新的class实例Personage设置为12)。与此相同:

println(person().age)

27
令人困惑的是,val可以更改a的内部状态,但不能更改val引用的对象。A val不是常数。
pferrel 2014年

5
为了进一步混淆事物,可以使用val(也许还有var,我没有尝试过)来定义一个函数。当使用def定义函数/方法时,每次调用def时都会对其进行评估。使用val时,仅在定义点进行评估。见stackoverflow.com/questions/18887264/...
melston

1
@melston是的,但方法功能也是不完全一样的东西
Jesper,2015年

3
为了进一步混淆,def也可以用于定义类的成员变量,而不必使用var。
Peiti Li

2
@pferrel并不真正令人困惑。与Java的最终版本相同。您可以将标记Listfinal,但可以修改其内容。
jFrenetic

100

我将从Scala在defvalvar之间的区别开始。

  • def-定义右侧内容的不可变标签,该标签延迟评估 -按名称评估。

  • val-为右侧内容定义一个不变的标签,该标签急切/立即评估 -通过值评估。

  • var-定义一个可变变量,最初设置为评估后的右侧内容。

例子,def

scala> def something = 2 + 3 * 4 
something: Int
scala> something  // now it's evaluated, lazily upon usage
res30: Int = 14

例子val

scala> val somethingelse = 2 + 3 * 5 // it's evaluated, eagerly upon definition
somethingelse: Int = 17

例如,var

scala> var aVariable = 2 * 3
aVariable: Int = 6

scala> aVariable = 5
aVariable: Int = 5

根据以上所述,无法重新分配defval中的标签,并且在进行任何尝试的情况下都会引发如下错误:

scala> something = 5 * 6
<console>:8: error: value something_= is not a member of object $iw
       something = 5 * 6
       ^

当类定义如下:

scala> class Person(val name: String, var age: Int)
defined class Person

然后用:

scala> def personA = new Person("Tim", 25)
personA: Person

将为该特定的Person实例(即“ personA”)创建一个不变标签。每当需要修改可变字段“年龄”时,这种尝试都会失败:

scala> personA.age = 44
personA.age: Int = 25

正如预期的那样,“年龄”是非可变标签的一部分。解决此问题的正确方法包括使用可变变量,如以下示例所示:

scala> var personB = new Person("Matt", 36)
personB: Person = Person@59cd11fe

scala> personB.age = 44
personB.age: Int = 44    // value re-assigned, as expected

很明显,从可变变量引用(即“ personB”)可以修改类的可变字段“ age”。

我仍然要强调一个事实,即一切都来自上述差异,任何Scala程序员都必须清楚这一点。


我认为以上解释不正确。查看其他答案。
Per Mildner '18

@PerMildner能否请您详细说明上述答案有什么问题?
Syed Souban

我不记得我最初的抱怨是什么。但是,答案的最后一部分是关于personAet al。似乎关闭。修改age成员是否起作用与您使用def personA还是使用无关var personB。不同之处在于,def personA如果您要修改Person从的第一次评估返回的-instance personA。该实例修改,但是当您再次求值时不会返回该实例personA。相反,您第二次这样做personA.age实际上是在做new Person("Tim",25).age
Per Mildner '19

29

def person = new Person("Kumar", 12) 

您正在定义一个函数/惰性变量,该变量始终返回名称为“ Kumar”且年龄为12的新Person实例。这完全有效,编译器没有理由抱怨。调用person.age将返回此新创建的Person实例的年龄,始终为12。

写作时

person.age = 45

您可以为Person类中的age属性分配一个新值,由于age被声明为,因此该值是有效的var。如果您尝试person使用新的Person对象重新分配,编译器将抱怨

person = new Person("Steve", 13)  // Error

是。通过在personA
Nilanjan Sarkar,

26

为了提供另一种观点,Scala中的“ def”表示每次使用时都会对其进行评估,而val是一次且仅进行一次评估的东西。在这里,表达def person = new Person("Kumar",12)意味着只要我们使用“人”,我们都会new Person("Kumar",12)接到一个电话。因此,两个“ person.age”自然是不相关的。

这就是我理解Scala的方式(可能以一种“更实用的”方式)。我不确定

def defines a method
val defines a fixed value (which cannot be modified)
var defines a variable (which can be modified)

确实是Scala想要表达的意思。我真的不喜欢这样,至少...


20

正如金太郎已经说过的那样,person是一个方法(由于def),并且总是返回一个新的Person实例。如您所知,如果将方法更改为var或val,它将起作用:

val person = new Person("Kumar",12)

另一种可能性是:

def person = new Person("Kumar",12)
val p = person
p.age=20
println(p.age)

但是,person.age=20在代码中,当您Personperson方法中获取实例时,是允许的,并且在此实例上,您可以更改a的值var。问题是,在该行之后,您将不再引用该实例(因为每次调用person都会产生一个新实例)。

这没什么特别的,在Java中您将具有完全相同的行为:

class Person{ 
   public int age; 
   private String name;
   public Person(String name; int age) {
      this.name = name;  
      this.age = age;
   }
   public String name(){ return name; }
}

public Person person() { 
  return new Person("Kumar", 12); 
}

person().age = 20;
System.out.println(person().age); //--> 12

8

让我们来看一下:

class Person(val name:String,var age:Int )
def person =new Person("Kumar",12)
person.age=20
println(person.age)

并用等效代码重写

class Person(val name:String,var age:Int )
def person =new Person("Kumar",12)
(new Person("Kumar", 12)).age_=(20)
println((new Person("Kumar", 12)).age)

看,def是一种方法。它将在每次调用时执行,并且每次将返回(a)new Person("Kumar", 12)。而且这些在“赋值”中没有错误,因为它实际上不是赋值,而只是对age_=方法的调用(由提供var)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.