ChatGPT解决这个技术问题 Extra ChatGPT

scala 中 def、val 和 var 的使用

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。为什么上面的第一组行不能正常工作,但也不会出错?


Y
Ying

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 时,它仅在定义点进行评估。请参阅stackoverflow.com/questions/18887264/…
@melston 是的,但是 methodfunction 也不完全是 the same thing
更让人迷惑的是,def也可以用来定义一个类的成员变量,不一定要用var。
@pferrel 并没有真正令人困惑。与 Java 的 final 相同。您可以将 List 标记为 final,但可以修改其内容。
P
Paolo Maresca

我将从 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 实例。此实例修改,但它不是您再次评估 personA 时返回的内容。相反,您第二次执行 personA.age 时,您实际上是在执行 new Person("Tim",25).age
K
Kintaro

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 方法很容易地证明
x
xji

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


L
Landei

正如 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

D
Daniel C. Sobral

让我们来看看:

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


关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅