248

Is there any difference between case object and object in scala?

Wojciech Durczyński
  • 2,627
  • 2
  • 16
  • 11
  • 3
    He has a point - it's not necessary to have a case object in order to be able to pattern match on it. I think this wasn't addressed in the previous question... – axel22 Mar 11 '11 at 09:03
  • 3
    I thought there would be a difference in pattern matching behaviour but both a case object and a normal object behave the same way in a pattern match AFAIK. It's pretty hard to find any information at all about case objects so I'm looking forward to someone enlightening us. – Age Mooij Mar 11 '11 at 10:12
  • 4
    It is not necessary to use `case` to have pattern matching, it is just sugar. Implementing `unapply` yourself does the job. – Raphael Mar 11 '11 at 19:06
  • 1
    The accepted answer just doesn't answer the question, as discussed in the comments on it. Way too late to make a difference, but should be noted. – itsbruce Jan 31 '17 at 13:57
  • It's not too late to edit the accepted answer. The edit will be reviewed and if relevant, accepted. – C4stor Jan 31 '17 at 15:11

7 Answers7

148

Here's one difference - case objects extend the Serializable trait, so they can be serialized. Regular objects cannot by default:

scala> object A
defined module A

scala> case object B
defined module B

scala> import java.io._
import java.io._    

scala> val bos = new ByteArrayOutputStream                                            
bos: java.io.ByteArrayOutputStream =  

scala> val oos = new ObjectOutputStream(bos)                                          
oos: java.io.ObjectOutputStream = java.io.ObjectOutputStream@e7da60                   

scala> oos.writeObject(B)

scala> oos.writeObject(A)
java.io.NotSerializableException: A$
axel22
  • 32,045
  • 9
  • 125
  • 137
  • 16
    I think that case objects can be serialized is the most difference from regular objects especially in the network communication between actors – 爱国者 Nov 15 '11 at 15:26
  • 17
    Adding `extends Serializable` should do the same trick though. – nilskp Mar 21 '13 at 14:23
114

Case classes differ from regular classes in that they get:

  1. pattern matching support
  2. default implementations of equals and hashCode
  3. default implementations of serialization
  4. a prettier default implementation of toString, and
  5. the small amount of functionality that they get from automatically inheriting from scala.Product.

Pattern matching, equals and hashCode don't matter much for singletons (unless you do something really degenerate), so you're pretty much just getting serialization, a nice toString, and some methods you probably won't ever use.

axel22
  • 32,045
  • 9
  • 125
  • 137
Dave Griffith
  • 20,435
  • 3
  • 55
  • 76
  • 4
    Having pattern matching makes a lot of sense if `case` is used in the "proper" use case: providing several implementations of one abstract class or trait. Some might be classes, some might be singleton objects. – Raphael Mar 11 '11 at 19:09
  • 3
    Sure, but that works just as well for case objects as non-case objects. – Dave Griffith Mar 12 '11 at 17:33
  • 49
    Point 3 and 4 of this answer is correct difference between case objects and objects. Point 1 and 2 doesn't matter for singleton objects. And singleton objects are always Products with arity 0 so point 5 doesn't matter also. – Wojciech Durczyński Mar 14 '11 at 10:50
  • 88
    This post perpetuates the myth that `object` is the same as a singleton. It is not. Rather it is exactly what it says it is, an object, i.e. a declaration and instantiation in one. This limits `object` to a single instance if defined in package scope, which effectively makes it a singleton, but only if defined IN THAT SCOPE. If defined inside a class, you can have as many instances as the class itself (it's lazily instantiated, so it's not necessarily 1-1). And those inner objects may very well be used as hash keys, making default equals/hashCode very much sensible. – nilskp Mar 21 '13 at 14:22
  • 73
    The question is about `case object` not class, why is this the correct answer? – User Nov 26 '15 at 15:51
  • 14
    This does not answer the question. This answer deals with the difference between `case class` and a `class`. The question is about the difference between `case object` and `object`. –  Dec 03 '15 at 13:40
  • Because the list of differences between case classes and classes IS the list of differences between case object and object. – C4stor Oct 03 '16 at 12:37
  • 6
    @C4stor The answer doesn't say that, though. An object is not a class. Given that case classes do a fair amount of magic behind the scenes, given Scala's various edge cases and complications, there's no reason simply to assume that the only difference between a standard Scala object and a case object is explained by what we know about the difference between standard classes and case classes. This answer doesn't even address the wording of the question. – itsbruce Jan 31 '17 at 13:50
  • 2
    In fact, some things behave differently for case objects than regular instances of case classes. Pattern matching behaves differently, for one thing. So the answer isn't even accurate. – itsbruce Jan 31 '17 at 13:57
  • 1
    It's not an answer to the question! –  Mar 21 '18 at 13:34
  • 1
    This is not the right answer it's about case class vs class difference – jeevan kishore Dec 23 '18 at 22:50
40
scala> object foo

defined object foo

scala> case object foocase

defined object foocase

Serialization difference:

scala> foo.asInstanceOf[Serializable]

java.lang.ClassCastException: foo$ cannot be cast to scala.Serializable
... 43 elided

scala> foocase.asInstanceOf[Serializable]

res1: Serializable = foocase

toString difference:

scala> foo

res2: foo.type = foo$@7bf0bac8

scala> foocase

res3: foocase.type = foocase

Jiri Tousek
  • 12,211
  • 5
  • 29
  • 43
3

A huge necro, but it is the highest result for this question in Google outside official tutorial which, as always, is pretty vague about the details. Here are some bare bones objects:

object StandardObject

object SerializableObject extends Serializable

case object CaseObject

Now, lets use the very useful feature of IntelliJ 'decompile Scala to Java' on compiled .class files:

//decompiled from StandardObject$.class
public final class StandardObject$ {
   public static final StandardObject$ MODULE$ = new StandardObject$();

   private StandardObject$() {
   }
}

//decompiled from StandardObject.class
import scala.reflect.ScalaSignature;

@ScalaSignature(<byte array string elided>)

public final class StandardObject {
}

As you can see, a pretty straightforward singleton pattern, except for reasons outside the scope of this question, two classes are generated: the static StandardObject (which would contain static forwarder methods should the object define any) and the actual singleton instance StandardObject$, where all methods defined in the code end up as instance methods. Things get more intresting when you implement Serializable:

//decompiled from SerializableObject.class
import scala.reflect.ScalaSignature;

@ScalaSignature(<byte array string elided>)
public final class SerializableObject {
}

        //decompiled from SerializableObject$.class
import java.io.Serializable;
import scala.runtime.ModuleSerializationProxy;

public final class SerializableObject$ implements Serializable {
   public static final SerializableObject$ MODULE$ = new SerializableObject$();

   private Object writeReplace() {
      return new ModuleSerializationProxy(SerializableObject$.class);
   }

   private SerializableObject$() {
   }
}

The compiler doesn't limit itself to simply making the 'instance' (non-static) class Serializable, it adds a writeReplace method. writeReplace is an alternative to writeObject/readObject; what it does, it serializes a different object whenether the Serializable class having this method is being serialized. On deserializention then, that proxy object's readResolve method is invoked once it is deserialized. Here, a ModuleSerializableProxy instance is serialized with a field carrying the Class[SerializableObject], so it knows what object needs to be resolved. The readResolve method of that class simply returns SerializableObject - as it is a singleton with a parameterless constructor, scala object is always structurally equal to itself between diffrent VM instances and different runs and, in this way, the property that only a single instance of that class is created per one VM instance is preserved. A thing of note is that there is a security hole here: no readObject method is added to SerializableObject$, meaning an attacker can maliciously prepare a binary file which matches standard Java serialization format for SerializableObject$ and a separate instance of the 'singleton' will be created.

Now, lets move to the case object:

//decompiled from CaseObject.class
import scala.collection.Iterator;
import scala.reflect.ScalaSignature;

@ScalaSignature(<byte array string elided>)
public final class CaseObject {
   public static String toString() {
      return CaseObject$.MODULE$.toString();
   }

   public static int hashCode() {
      return CaseObject$.MODULE$.hashCode();
   }

   public static boolean canEqual(final Object x$1) {
      return CaseObject$.MODULE$.canEqual(var0);
   }

   public static Iterator productIterator() {
      return CaseObject$.MODULE$.productIterator();
   }

   public static Object productElement(final int x$1) {
      return CaseObject$.MODULE$.productElement(var0);
   }

   public static int productArity() {
      return CaseObject$.MODULE$.productArity();
   }

   public static String productPrefix() {
      return CaseObject$.MODULE$.productPrefix();
   }

   public static Iterator productElementNames() {
      return CaseObject$.MODULE$.productElementNames();
   }

   public static String productElementName(final int n) {
      return CaseObject$.MODULE$.productElementName(var0);
   }
}

        //decompiled from CaseObject$.class
import java.io.Serializable;
import scala.Product;
import scala.collection.Iterator;
import scala.runtime.ModuleSerializationProxy;
import scala.runtime.Statics;
import scala.runtime.ScalaRunTime.;

public final class CaseObject$ implements Product, Serializable {
   public static final CaseObject$ MODULE$ = new CaseObject$();

   static {
      Product.$init$(MODULE$);
   }

   public String productElementName(final int n) {
      return Product.productElementName$(this, n);
   }

   public Iterator productElementNames() {
      return Product.productElementNames$(this);
   }

   public String productPrefix() {
      return "CaseObject";
   }

   public int productArity() {
      return 0;
   }

   public Object productElement(final int x$1) {
      Object var2 = Statics.ioobe(x$1);
      return var2;
   }

   public Iterator productIterator() {
      return .MODULE$.typedProductIterator(this);
   }

   public boolean canEqual(final Object x$1) {
      return x$1 instanceof CaseObject$;
   }

   public int hashCode() {
      return 847823535;
   }

   public String toString() {
      return "CaseObject";
   }

   private Object writeReplace() {
      return new ModuleSerializationProxy(CaseObject$.class);
   }

   private CaseObject$() {
   }
}

A lot more is going on, as CaseObject$ now implements also Product0, with its iterator and accessor methods. I am unaware of a use case for this feature, it is probably done for consistency with case class which is always a product of its fields. The main practical difference here is that we get canEqual, hashCode and toString methods for free. canEqual is relevant only if you decide to compare it with a Product0 instance which is not a singleton object, toString saves us from implementing a single simple method, which is useful when case objects are used as enumeration constants without any behaviour implemented. Finally, as one might suspect, hashCode returns a constant, so it is the same for all VM instances. This matters if one serializes some flawed hash map implementation, but both standard java and scala hash maps wisely rehash all contents on deserialization, so it shouldn't matter. Note that equals is not overriden, so it is still reference equality, and that the security hole is still there. A huge caveat here: if a case object inherit equals/toString from some supertype other than Object, the corresponding methods are not generated, and the inherited definitions are used instead.

TL;DR: the only difference that matters in practice is the toString returning the unqualified name of the object.

I must make a disclamer here, though: I cannot guarantee that the compiler doesn't treat case objects specially in addition to what is actually in the bytecode. It certainly does so when patterm matching case classes, aside from them implementing unapply.

Turin
  • 2,208
  • 15
  • 23
2

It's similar with case class and class ,we just use case object instead of case class when there isn't any fields representing additional state information.

wineway
  • 39
  • 1
2

case objects implicitly come with implementations of methods toString, equals, and hashCode, but simple objects don't. case objects can be serialized while simple objects cannot, which makes case objects very useful as messages with Akka-Remote. Adding the case keyword before object keyword makes the object serializable.

1

We know objects and "case class" before. But "case object" is a mix of both i.e it is a singleton similar to an object and with a lot of boilerplate as in a case class. The only difference is that the boilerplate is done for an object instead of a class.

case objects won't come with the below ones:

Apply, Un-apply methods. here are no copy methods since this is a singleton. No method for structural equality comparison. No constructor as well.

jeevan kishore
  • 320
  • 1
  • 4
  • 16