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
If an object performs stateful computations on the inside or exhibits other kinds of complex behaviour, it should be an ordinary class.
-------
Those are the only differences to regular classes.
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
If an object performs stateful computations on the inside or exhibits other kinds of complex behaviour, it should be an ordinary class.
-------
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 such types are trees. A binary tree, for instance, can be implemented like this:
And they can also be used with hash maps or sets, since they have a valid, stable hashCode.
---------
A very simple example of such types are trees. A binary tree, for instance, can be 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.
---------
- Case classes can be pattern matched
- Case classes automatically define hashcode and equals
- Case classes automatically define getter methods for the constructor arguments.
Those are the only differences to regular classes.
No comments:
Post a Comment