开发者

Scala naming convention for "setters" on immutable objects

I do not know what to call my "setters" on immutable objects?

For a mutable object Person, setters work like this:

class Person(private var _name: String) {
  def name = "Mr " + _name
  def name_=(newName: String) {
    _name = newName
  }
}

val p = new Person("Olle")
println("Hi "+ p.name)
p.name = "Pelle"
println("Hi "+ p.name)

This is all well and good, but what if Person is immutable?

class Person(private val _name: String) {
  def name = "Mr " + _name
  def whatHereName(newName: String): Person = new Person(newName)
}

val p = new Person("Olle")
println("Hi "+ p.name)
val p2 = p.whatHereName("Pell开发者_运维问答e")
println("Hi "+ p2.name)

What should whatHereName be called?

EDIT: I need to put stuff in the "setter" method, like this:

class Person(private val _name: String) {
  def name = "Mr " + _name
  def whatHereName(newName: String): Person = {
    if(name.length > 3)
      new Person(newName.capitalize)
    else
      throw new Exception("Invalid new name")
  }
}

The real code is much bigger than this, so a simple call to the copy method will not do.

EDIT 2:

Since there are so many comments on my faked example (that it is incorrect) I better give you the link to the real class (Avatar).

The "setter" methods I don't know what to call are updateStrength, updateWisdom ... but I will probably change that to withStrength soon..


I like the jodatime way. that would be withName.

val p = new Person("Olle")
val p2 = p.withName("kalle");

more jodatime examples: http://joda-time.sourceforge.net/


Scala case classes have autogenerated method copy for this purpose. It's used like this:

val p2 = p.copy(name = "Pelle")


If you need to perform validation, etc. when 'modifying' a field, then why should this be any different from validation when you first create the object?

In this case, you can then put the necessary validation/error-throwing logic in the constructor of a case class, and this will be used whenever a new instance is created via the copy method.


You could define a single method for that. Either copy, or, in case it is already a case class, with:

class Person(private val _name: String) {
  def name = "Mr " + _name
  def copy(name: String = _name): Person = new Person(name)
}

EDIT

The copy method on the linked example should look like this:

// Setters
def copy(strength: Int = features.strength,
         wisdom: Int = features.wisdom,
         charisma: Int = features.charisma,
         hitpoints: Int = features.hitpoints): Avatar = {
  if (hitpoints != features.hitpoints)
    println("setHitpoints() old value: " + features.hitpoints + ", new value: " + hitpoints)

  if (hitpoints > 0) 
    updateCreatureFeature(
      features.copy(strength = strength,
                    wisdom = wisdom,
                    charisma = charisma,
                    hitpoints = hitpoints))
  else
    throw new DeathException(name + " died!")

  // Alternate coding (depend on thrown exception on "check"):
  // check(strength, wisdom, charisma, hitpoints)
  // updateCreateFeature(...)
}


Adding to Oleg answer, you would write the class like this:

case class Person(name: String) //That's all!

You would use it like this:

val p = Person("Olle") // No "new" necessary
println("Hi" + p.name)
val p2 = p.copy(name="Pelle")
println("Hi" + p2.name)    

Using the copy method like above is possible, but in your simple case I would just use:

val p2 = Person("Pelle")

The copy methods show their strengths if you have classes like:

case class Person(name: String, age: Int, email: EMail, pets: List[Pet] = List())
val joe = Person("Joe", 41, EMail("joe@example.com"))
val joeAfterHisNextBirthday = joe.copy(age=42)


As for now I am using update<Field> name convention for all "setter"-like methods on immutable objects.

I can not use set<Field> since it reminds too much about the mutable setters in Java.

How do you feel about using update<Field> for all methods that returns a new instance of the same identity as the current instance?


Though previous answers resolve the problem, I would like to share how I deal with immutable objects (which is only syntactic sugar).

To have a clearer syntax (IMHO) I implement the apply method in immutable classes, returning the result of the copy method in case classes and a new instance when it is a regular class. ie:

import java.util.Date

class Tournament (val name: String, val start: Date) {
  /* With case class
  def apply (name: String = this.name, start: Date = this.start) =
    copy (name, start)
  */

  def apply (name: String = this.name, start: Date = this.start) =
    new Tournament (name, start)

  override def toString () = s"${name} at ${start}"
}

object Main extends App {
  val tour = new Tournament ("Euroleague", new Date)
  val tour2 = tour (name = tour.name + " 2014")
  println (tour)
  println (tour2)
}

This makes the "mutator" method the default method for any instance of that class.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜