scala 中 def、val 和 var 的使用

Posted

技术标签:

【中文标题】scala 中 def、val 和 var 的使用【英文标题】:Use of def, val, and var in scala 【发布时间】:2011-05-25 03:43:30 【问题描述】:
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 person = new Person("Kumar",12) 中使用了def。如果我使用 var 或 val,则输出为 20。我知道在 scala 中默认是 val 。这个:

def age = 30
age = 45

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

【问题讨论】:

【参考方案1】:

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

def 定义了一个方法 val 定义了一个固定的(不能修改) var 定义了一个变量(可以修改)

查看您的代码:

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

这定义了一个名为person 的新方法。您只能在没有() 的情况下调用此方法,因为它被定义为无参数方法。对于空括号方法,您可以使用或不使用“()”来调用它。如果你只是写:

person

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

person.age = 20

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

最后一行:

println(person.age)

在这里您再次调用person 方法,该方法返回类Person 的新实例(age 设置为12)。和这个是一样的:

println(person().age)

【讨论】:

为了混淆,val 的内部状态可以更改,但 val 引用的对象不能。 val 不是常数。 为了进一步混淆事情,val(也许还有var,我没试过)可以用来定义一个函数。当使用 def 定义函数/方法时,每次调用 def 的主体时都会对其进行评估。使用 val 时,它仅在定义点进行评估。见***.com/questions/18887264/… @melston 是的,但是 methodfunction 也不完全是 the same thing。 更让人迷惑的是,def也可以用来定义一个类的成员变量,不一定要用var。 @pferrel 并没有真正令人困惑。与 Java 的 final 相同。您可以将List 标记为final,但可以修改其内容。【参考方案2】:

我先从 Scala 中 defvalvar 之间的区别开始。

def - 为右侧内容定义一个不可变标签惰性评估 - 按名称评估。

val - 为右侧内容定义一个不可变标签急切/立即评估 - 按价值评估。

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

示例,定义

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

示例,变量

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 程序员都必须清楚这一点。

【讨论】:

我认为上面的解释是不正确的。查看其他答案。 @PerMildner 您能否详细说明上述答案中有什么问题? 我不记得我最初的投诉是什么。但是,答案的最后一部分,关于personA 等。似乎关闭了。修改age 成员是否有效与您使用def personA 还是var personB 无关。不同之处在于,在def personA 的情况下,您正在修改从首次评估personA 返回的Person-instance。此实例修改,但它不是您再次评估personA 时返回的内容。相反,您第二次执行personA.age 时实际上是在执行new Person("Tim",25).age【参考方案3】:

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 上调用 hashCode 方法来轻松演示【参考方案4】:

从另一个角度来看,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 想要表达的意思。至少我真的不喜欢这样想……

【讨论】:

【参考方案5】:

正如 Kintaro 已经说过的,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 是允许的,因为您从person 方法返回一个Person 实例,并且在此实例上您可以更改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

【讨论】:

【参考方案6】:

让我们来看看这个:

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 提供)。

【讨论】:

以上是关于scala 中 def、val 和 var 的使用的主要内容,如果未能解决你的问题,请参考以下文章

Scala面试题汇总

scala 入门

类对象基础类型操作(Scala)

何时在 Scala 特征中使用 val 或 def?

scala--

scala中var和val的区别