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.
精彩评论