Use of def, val, and var in scala

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

These lines of code outputs 12, even though person.age=20 was successfully executed. I found that this happens because I used def in def person = new Person("Kumar",12). If I use var or val the output is 20. I understand the default is val in scala. This:

def age = 30
age = 45

...gives a compilation error because it is a val by default. Why do the first set of lines above not work properly, and yet also don't error?

105689 次浏览

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

正如 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方法返回一个 Person实例时,允许在代码中使用 person.age=20,并且在此实例中允许更改 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

在 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)

让我们看看这个:

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 表示 立即评估,并且只有一次的内容。在这里,表达式 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 中存在的 绝对的ValVar之间的区别开始。

  • 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

例如,var

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


scala> aVariable = 5
aVariable: Int = 5

根据上述规定,绝对的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’)创建一个 不可变的标签。每当需要修改可变字段‘ age’时,这种尝试就会失败:

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