【问题标题】:Use of def, val, and var in scalascala 中 def、val 和 var 的使用
【发布时间】: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。为什么上面的第一组行不正常,但也不出错?

【问题讨论】:

    标签: scala


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

    我先从 Scala 中 defvalvar 之间的区别开始。

    • 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
    

    根据上述,来自 defval 的标签不能被重新分配,并且在任何尝试的情况下都会引发如下错误:

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

        【讨论】:

          猜你喜欢
          • 2018-10-01
          • 2018-08-15
          • 1970-01-01
          • 2013-04-25
          • 1970-01-01
          • 2013-11-07
          • 2012-09-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多