views:

151

answers:

4

I'm beginning Scala. Am I correct understanding that I should define a class as a case class if I'd like it's arguments to be exposed as properties? Does not it introduce any side effects?

+7  A: 

You will get properites for any parameters defined and they will be vals (i.e., finals)

case class User(name: String, group: String)
val user = User("jsmith", "admins")

// access both properties
println("name: %s group: %s".format(user.name, user.group))

You can get this behavior with regular (i.e., non-case classes) as well:

// creates two final public properties as well
class User(val name: String, val group: String)

// creates read/write public properties
class User(var name: String, var group: String)
val user = new User("jsmith", "admins")
user.group = "guests"

Case classes also bring a lot of other things as well such as a useful implementations of equality, hashcode and toString and a companion object with a factory method that eliminates the need to use new among other things.

As you can see, a case class is not required to achieve what you want but it gets you there quickly. As for side effects, defining a case class generates some bit of code behind the scenes to give you what was described in the previous paragraph. These are often useful and I tend not to worry about them but it is good to know about them.

dpp
Thanks. But are there any side effects which can cause unexpected behaviour (leading into bugs then)? Why would one use non-case classes in usual cases?
Ivan
+6  A: 

The boilerplate code generated for case classes carries a small but non-zero cost in bytecode. In addition to the copy method, there is hashCode, equals and toString as well as the companion object factory method.

More significant is the fact that it is inadvisable to derive classes from case classes. Deriving a case class from a case class really invites problems (and the compiler will yell at you). In particular, no overriding copy(...) method is generated by the compiler, so you can get some odd failure modes if you try to copy a case class derived from a case class.

If you keep your case classes at the leafs of any inheritance graphs, you'll be fine.

Randall Schulz
+5  A: 

Additional to the already mentioned points, a case class is conceptually close to what you would call a "value class" in Java. Of course nothing stops you from writing mutable case classes or case classes with heavy functionality but few data, but this could surprise others working with your code. As a rule of thumb I'd say at least think twice before creating case classes...

  • which need mutable state
  • when you are not sure if you might need subclasses later
  • if their main purpose is to calculate things (not to represent things), and these calculations are complicated
  • if you need fine grained control over their behavior (say, you need custom pattern matching)
  • when performance is really important
  • when you need only one of the "case class feature", e.g. I'd consider using a case class just to avoid typing "val" in front of constructor args as overkill
Landei
Could you please explain why to avoid case classes when performance is really important?
ziggystar
1) Case classes need to do a little bit more work on construction. 2) The implementations for ##, equals, toString etc. need to be quite elaborated to avoid problems in "edgy" cases, so you can probably save a few ops by implementing them yourself.Note that I'm talking about minimal performance differences that may matter for things like vectors and matrices for 3D graphics, but not for "normal" applications.
Landei
+3  A: 

The other answers are all fine, but the one real risk they miss is that case classes have value equality, which behaves very differently from standard object-oriented identity equality. Two case objects are equal iff all of their fields are equal. This is exactly what is needed in some cases, but very unexpected in others. In particular, adding mutable state to something with value equality is asking for trouble. You could easily find yourself with objects whose hashcode changes with time, resulting in corrupted HashMaps and other such nastiness.

Declaring something a case class in order to save a few keystrokes declaring your properties as "val" is false economy, and will someday bite you.

Dave Griffith