Misusage Mistake
Thinking of var
and val
as fields.
Scala enforces the Uniform Access Principle, by making it impossible to refer to a field directly. All accesses to any field are made through getters and setters. What val
and var
actually do is define a field, a getter for that field, and, for var
, a setter for that field.
Java programmers will often think of var
and val
definitions as fields, and get surprised when they discover that they share the same namespace as their methods, so they can't reuse their names. What share the same namespace is the automatically defined getter and setter, not the field. Many times they then try to find a way to get to the field, so that they can get around that limitation -- but there's no way, or the uniform access principle would be violated.
Another consequence of it is that, when subclassing, a val
can override a def
. The other way around is not possible, because a val
adds the guarantee of immutability, and def
doesn't.
There's no guideline on what to call private getters and setters when you need to override it for some reason. Scala compiler and library code often use nicknames and abbreviation for private values, as opposed to fullyCamelNamingConventions for public getters and setters. Other suggestions include renaming, singletons inside an instance, or even subclassing. Examples of these suggestions:
Renaming
class User(val name: String, initialPassword: String) {
private lazy var encryptedPassword = encrypt(initialPassword, salt)
private lazy var salt = scala.util.Random.nextInt
private def encrypt(plainText: String, salt: Int): String = { ... }
private def decrypt(encryptedText: String, salt: Int): String = { ... }
def password = decrypt(encryptedPassword, salt)
def password_=(newPassword: String) = encrypt(newPassword, salt)
}
Singleton
class User(initialName: String, initialPassword: String) {
private object fields {
var name: String = initialName;
var password: String = initialPassword;
}
def name = fields.name
def name_=(newName: String) = fields.name = newName
def password = fields.password
def password_=(newPassword: String) = fields.password = newPassword
}
alternatively, with a case class, which will automatically define methods for equality, hashCode, etc, which can then be reused:
class User(name0: String, password0: String) {
private case class Fields(var name: String, var password0: String)
private object fields extends Fields(name0, password0)
def name = fields.name
def name_=(newName: String) = fields.name = newName
def password = fields.password
def password_=(newPassword: String) = fields.password = newPassword
}
Subclassing
case class Customer(name: String)
class ValidatingCustomer(name0: String) extends Customer(name0) {
require(name0.length < 5)
def name_=(newName : String) =
if (newName.length < 5) error("too short")
else super.name_=(newName)
}
val cust = new ValidatingCustomer("xyz123")
Thanks to Kevin Wright for the last two examples.