views:

368

answers:

3

I searched in google to find the differences between the two, every one mentions that when you want to do pattern matching on the class use case class else use classes and also mentioning some extra perks like equals and hascode overriding. But are these the only reasons why one should use a case class instead of class? I guess there should be some very important reason for this feature in Scala. Can somebody give a proper explanation or point to a right resource to learn more about the scala case classes.

+10  A: 

Case classes can be seen as plain and immutable data-holding objects that should exclusively depend on their constructor arguments.

This functional concept allows us to

  • use a compact initialisation syntax (Node(1, Leaf(2), None)))
  • decompose them using pattern matching
  • have equality comparisons implicitly defined

In combination with inheritance, case classes are used to mimic algebraic datatypes.

If an object performs stateful computations on the inside or exhibits other kinds of complex behaviour, it should be an ordinary class.

Dario
@Dario Thanks for the pointers. So ADT's are something like Enums?
Teja Kantamneni
@Teja: In some way. ADT's are kinda *parameterized enums*, extremely powerful and typesafe.
Dario
Sealed case classes are used to mimic algebraic datatypes. Otherwise the number of subclasses is not limited.
Thomas Jung
@Thomas, actually, case classes which extend sealed abstract classes are limited.
Daniel
@Thomas: Correctly spoken, *case classes deriving from sealed abstract classes mimic closed algebraic datatypes* whereas the ADT is otherwise *open*.
Dario
@Dario ... and the type is otherwise open and not and a ADT. :-)
Thomas Jung
@Thomas: Yep, it's merely an existential ;)
Dario
+2  A: 
  • Case classes can be pattern matched
  • Case classes automatically define hashcode and equals
  • Case classes automatically define getter methods for the constructor arguments.

(You already mentioned all but the last one).

Those are the only differences to regular classes.

sepp2k
Setters are not generated for case classes unless "var" is specified in the constructor argument, in which case you get the same getter/setter generation as regular classes.
Mitch Blevins
@Mitch: True, my bad. Fixed now.
sepp2k
+2  A: 

Technically, there is no difference between a class and a case class -- even if the compiler does optimize some stuff when using case classes. However, a case class is used to do away with boiler plate for a specific pattern, which is implementing algebraic data types.

A very simple example of one such types are trees. A binary tree, for instance, can implemented like this:

sealed abstract class Tree
case class Node(left: Tree, right: Tree) extends Tree
case class Leaf[A](value: A) extends Tree
case object EmptyLeaf extends Tree

That enable us to do the following:

// DSL-like assignment:
val treeA = Node(EmptyLeaf, Leaf(5))
val treeB = Node(Node(Leaf(2), Leaf(3)), Leaf(5))

// On Scala 2.8, modification through cloning:
val treeC = treeA.copy(left = treeB.left)

// Pretty printing:
println("Tree A: "+treeA)
println("Tree B: "+treeB)
println("Tree C: "+treeC)

// Comparison:
println("Tree A == Tree B: %s" format (treeA == treeB).toString)
println("Tree B == Tree C: %s" format (treeB == treeC).toString)

// Pattern matching:
treeA match {
  case Node(EmptyLeaf, right) => println("Can be reduced to "+right)
  case Node(left, EmptyLeaf) => println("Can be reduced to "+left)
  case _ => println(treeA+" cannot be reduced")
}

// Pattern matches can be safely done, because the compiler warns about
// non-exaustive matches:
def checkTree(t: Tree) = t match {
  case Node(EmptyLeaf, Node(left, right)) =>
  // case Node(EmptyLeaf, Leaf(el)) =>
  case Node(Node(left, right), EmptyLeaf) =>
  case Node(Leaf(el), EmptyLeaf) =>
  case Node(Node(l1, r1), Node(l2, r2)) =>
  case Node(Leaf(e1), Leaf(e2)) =>
  case Node(Node(left, right), Leaf(el)) =>
  case Node(Leaf(el), Node(left, right)) =>
  // case Node(EmptyLeaf, EmptyLeaf) =>
  case Leaf(el) =>
  case EmptyLeaf =>
}

Note that trees construct and deconstruct (through pattern match) with the same syntax, which is also exactly how they are printed (minus spaces).

And they can also be used with hash maps or sets, since they have a valid, stable hashCode.

Daniel