简体   繁体   English

在scala中使用def,val和var

[英]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. 这些代码行输出12 ,即使person.age=20已成功执行。 I found that this happens because I used def in def person = new Person("Kumar",12) . 我发现这是因为我在def person = new Person("Kumar",12)使用了def person = new Person("Kumar",12) If I use var or val the output is 20 . 如果我使用var或val,则输出为20 I understand the default is val in scala. 我理解scala中的默认值是val。 This: 这个:

def age = 30
age = 45

...gives a compilation error because it is a val by default. ...给出编译错误,因为它默认为val。 Why do the first set of lines above not work properly, and yet also don't error? 为什么上面的第一组行不能正常工作,而且还没有错误?

There are three ways of defining things in Scala: 在Scala中有三种定义方法:

  • def defines a method def定义了一种方法
  • val defines a fixed value (which cannot be modified) val定义一个固定 (不能修改)
  • var defines a variable (which can be modified) var定义一个变量 (可以修改)

Looking at your code: 看看你的代码:

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

This defines a new method called person . 这定义了一个名为person的新方法。 You can call this method only without () because it is defined as parameterless method. 只能在没有()情况下调用此方法,因为它被定义为无参数方法。 For empty-paren method, you can call it with or without '()'. 对于empty-paren方法,你可以使用或不使用'()'来调用它。 If you simply write: 如果你只是写:

person

then you are calling this method (and if you don't assign the return value, it will just be discarded). 那么你正在调用这个方法(如果你没有分配返回值,它将被丢弃)。 In this line of code: 在这行代码中:

person.age = 20

what happens is that you first call the person method, and on the return value (an instance of class Person ) you are changing the age member variable. 所发生的是你首先调用person方法,并在返回值(类Person的实例)上更改age成员变量。

And the last line: 最后一行:

println(person.age)

Here you are again calling the person method, which returns a new instance of class Person (with age set to 12). 在这里,您再次调用person方法,该方法返回类Person的新实例( age设置为12)。 It's the same as this: 它与此相同:

println(person().age)

I'd start by the distinction that exists in Scala between def , val and var . 我首先介绍了defvalvar之间Scala中存在的区别。

  • def - defines an immutable label for the right side content which is lazily evaluated - evaluate by name. def - 为懒惰评估的右侧内容定义不可变标签 - 按名称评估。

  • val - defines an immutable label for the right side content which is eagerly/immediately evaluated - evaluated by value. val - 为急剧/立即评估的右侧内容定义不可变标签 - 按值评估。

  • var - defines a mutable variable , initially set to the evaluated right side content. var - 定义一个可变变量 ,最初设置为评估的右侧内容。

Example, def 例如,def

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

Example, val 例如,val

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

Example, var 例子,var

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

scala> aVariable = 5
aVariable: Int = 5

According to above, labels from def and val cannot be reassigned, and in case of any attempt an error like the below one will be raised: 根据上面的说法, defval的标签不能重新分配,如果有任何尝试,将引发如下所示的错误:

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

When the class is defined like: 当类被定义为:

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

and then instantiated with: 然后实例化:

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

an immutable label is created for that specific instance of Person (ie 'personA'). 为Person的特定实例(即'personA')创建不可变标签 Whenever the mutable field 'age' needs to be modified, such attempt fails: 每当需要修改可变字段'age'时,此类尝试将失败:

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

as expected, 'age' is part of a non-mutable label. 正如预期的那样,'年龄'是不可变标签的一部分。 The correct way to work on this consists in using a mutable variable, like in the following example: 处理此问题的正确方法包括使用可变变量,如下例所示:

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

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

as clear, from the mutable variable reference (ie 'personB') it is possible to modify the class mutable field 'age'. 很明显,从可变变量引用 (即'personB')可以修改类可变字段'age'。

I would still stress the fact that everything comes from the above stated difference, that has to be clear in mind of any Scala programmer. 我仍然会强调,所有内容都来自上述差异,任何Scala程序员都必须清楚这一点。

With

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

you are defining a function/lazy variable which always returns a new Person instance with name "Kumar" and age 12. This is totally valid and the compiler has no reason to complain. 你正在定义一个函数/惰性变量,它总是返回一个名为“Kumar”且年龄为12的新Person实例。这是完全有效的,编译器没有理由抱怨。 Calling person.age will return the age of this newly created Person instance, which is always 12. 调用person.age将返回此新创建的Person实例的年龄,该实例始终为12。

When writing 写作时

person.age = 45

you assign a new value to the age property in class Person, which is valid since age is declared as var . 您为类Person中的age属性分配一个新值,该值有效,因为age声明为var The compiler will complain if you try to reassign person with a new Person object like 如果您尝试使用新的Person对象重新分配person ,编译器会抱怨

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

To provide another perspective, "def" in Scala means something that will be evaluated each time when it's used, while val is something that is evaluated immediately and only once . 为了提供另一个视角,Scala中的“def”表示每次使用都要评估的内容,而val是立即评估并且只评估一次的内容 Here, the expression def person = new Person("Kumar",12) entails that whenever we use "person" we will get a new Person("Kumar",12) call. 这里,表达def person = new Person("Kumar",12)需要每当我​​们使用“person”时,我们将获得一个new Person("Kumar",12)调用。 Therefore it's natural that the two "person.age" are non-related. 因此,两个“person.age”是无关的,这是很自然的。

This is the way I understand Scala(probably in a more "functional" manner). 这是我理解Scala的方式(可能是以更“功能”的方式)。 I'm not sure if 我不确定

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

is really what Scala intends to mean though. 实际上是斯卡拉的意思。 I don't really like to think that way at least... 我真的不喜欢这样思考至少......

As Kintaro already says, person is a method (because of def) and always returns a new Person instance. 正如金太郎所说,人是一种方法(因为def)并且总是返回一个新的Person实例。 As you found out it would work if you change the method to a var or val: 如您发现,如果将方法更改为var或val,它将起作用:

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

Another possibility would be: 另一种可能性是:

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

However, person.age=20 in your code is allowed, as you get back a Person instance from the person method, and on this instance you are allowed to change the value of a var . 但是,当您从person方法返回Person实例时,允许在您的代码中使用person.age=20 ,并且在此实例上,您可以更改var的值。 The problem is, that after that line you have no more reference to that instance (as every call to person will produce a new instance). 问题是,在那一行之后你没有更多的引用那个实例(因为每次调用person都会产生一个新的实例)。

This is nothing special, you would have exactly the same behavior in Java: 这没什么特别的,你在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

Let's take this: 我们来看看:

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

and rewrite it with equivalent code 并用等效代码重写它

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)

See, def is a method. 看, def是一种方法。 It will execute each time it is called, and each time it will return (a) new Person("Kumar", 12) . 它将在每次调用时执行,并且每次它将返回(a) new Person("Kumar", 12) And these is no error in the "assignment" because it isn't really an assignment, but just a call to the age_= method (provided by var ). 这些在“赋值”中没有错误,因为它实际上不是赋值,而只是调用age_=方法(由var提供)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM