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。为什么上面的第一组行不能正常工作,但也不会出错?
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)
我将从 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 程序员都必须清楚这一点。
personA
等。似乎关闭了。修改 age
成员是否有效与您使用 def personA
还是 var personB
无关。不同之处在于,在 def personA
情况下,您正在修改从您对 personA
的第一次评估返回的 Person
实例。此实例已修改,但它不是您再次评估 personA
时返回的内容。相反,您第二次执行 personA.age
时,您实际上是在执行 new Person("Tim",25).age
。
和
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
换个角度来看,Scala 中的“def”意味着在使用时每次都会被评估,而 val 是立即且仅被评估一次的东西。在这里,表达式 def person = new Person("Kumar",12)
意味着每当我们使用“person”时,我们都会得到一个 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 想要表达的意思。至少我真的不喜欢这样想……
正如 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
让我们来看看:
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
提供)的调用。
val
的内部状态可以更改,但 val 引用的对象不能。val
不是常数。List
标记为final
,但可以修改其内容。