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 是的,但是 method 和 function 也不完全是 the same thing。
更让人迷惑的是,def也可以用来定义一个类的成员变量,不一定要用var。
@pferrel 并没有真正令人困惑。与 Java 的 final 相同。您可以将List
标记为final
,但可以修改其内容。【参考方案2】:
我先从 Scala 中 def、val 和 var 之间的区别开始。
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
根据上述,来自 def 和 val 的标签不能被重新分配,并且在任何尝试的情况下都会引发如下错误:
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 的使用的主要内容,如果未能解决你的问题,请参考以下文章