1

I am Scala beginner. Below Scala code runs well, but I cannot understand it. Log shows, line 19 finally run to line 12. How could it be?

object TestClassDef {
  class Person {
    private var _age = 0
    def age:Int = _age
    def age_=(newAge: Int) = {
      _age = newAge
      println("age changed to " + _age)       // line 12
    }
  }

  def main(args: Array[String]) {
    var p = new Person()
//    p.age_=(25)
    p.age = 26                                // line 19
  }
}
wizardlee
  • 53
  • 6

3 Answers3

1

If I get your question correctly, you're surprised that the method is called when you assign the value on line 19. This is because the _= (at the end of the age function with an Int parameter) means that it's an assignment operator (also see What are all the uses of an underscore in Scala?) so it does make sense that it's called when you simply type p.age = 26.

Community
  • 1
  • 1
Sebastiaan van den Broek
  • 5,818
  • 7
  • 40
  • 73
1

This is mutator

def age_=(newAge: Int) = {
  _age = newAge
  println("age changed to " + _age)   
}

So the call

 p.age = 26 

is converted by compiler to

 p.age_=(26)

which makes call to mutator.

Mutator naming conventions from http://docs.scala-lang.org/style/naming-conventions.html

For mutators, the name of the method should be the name of the property with “_=” appended. As long as a corresponding accessor with that particular property name is defined on the enclosing type, this convention will enable a call-site mutation syntax which mirrors assignment. Note that this is not just a convention but a requirement of the language.

Also to see what compiler is creating pass -Xprint:typer to the Scala compiler. For reference the above code with this parameter generates:

package <empty> {
  object TestClassDef extends scala.AnyRef {
    def <init>(): TestClassDef.type = {
      TestClassDef.super.<init>();
      ()
    };
    class Person extends scala.AnyRef {
      def <init>(): TestClassDef.Person = {
        Person.super.<init>();
        ()
      };
      private[this] var _age: Int = 0;
      <accessor> private def _age: Int = Person.this._age;
      <accessor> private def _age_=(x$1: Int): Unit = Person.this._age = x$1;
      def age: Int = Person.this._age;
      def age_=(newAge: Int): Unit = {
        Person.this._age_=(newAge);
        scala.this.Predef.println("age changed to ".+(Person.this._age))
      }
    };
    def main(args: Array[String]): Unit = {
      var p: TestClassDef.Person = new TestClassDef.this.Person();
      p.age_=(26)
    }
  }
}
Avinash Tiwari
  • 135
  • 1
  • 10
0

Scala allows non-alphanumerics in identifiers (in two ways):

case 1:

(specialcharacters)*

scala> val ##*# = 1000000
scala> val #Alpha# = 1  // mixing alphanumerics not allowed

case 2:

letter(alphanumerics)* _ (specialcharacters)*

scala> val k12? = 1       // doesn't work, _ is mandatory
scala> val k12_? = 1
scala> val k12_?*&^% = 1
scala> val k12_?Alpha = 1 // doesn't work, can't mix alphanumeric

Special cases:

Some symbols/combination-of-symbols are reserved e.g. = # <%

scala> val # =  1   // doesn't work
scala> val ### = 1  // works fine

scala> val <% = 1   // doesn't work
scala> val <%> = 1  // works fine

Some symbol-combinations are special, _= can be used in method names but not in variable names.

 scala> val k_= = 1  // doesn't work
 scala> def k_= = 1  // works fine

method names ending in _= are setters.

if a class/object's setter method def x_= is defined along with a parameter-less method of same name def x, then x = e is interpreted as x_=(e)

Shyamendra Solanki
  • 8,751
  • 2
  • 31
  • 25