The reflection

It turned out that Morphia uses Java reflection to instantiate the objects fetched from MongoDB:

Since Morphia is a Java library, it has no way to be aware of something like a Scala object — it just creates a new instance of whatever class it encounters.

Also, from the JVM perspective, an object is just a class that, actually, has a private constructor, so nothing can stop you (or Morphia) from manually creating a new instance using reflection.

Let’s now digress a bit from the main story and see what a compiled object actually looks like.

The anatomy of an object

If you compile the Notification ADT, you’ll get a couple of resulting classes:

$ scalac Notifications.scala $ ls -1 *.class

Email$.class

Email.class

Notification.class

Slack$.class

Slack.class

If you have never seen a compiled object before, you may be surprised by the fact that the compiler not only generated Slack.class (which you would probably expect), but also Slack$.class .

Let’s inspect those two classes with javap — a command line tool in the JDK that lets you disassemble class files:

$ javap -p Slack.class

Compiled from "Notifications.scala"

public final class Slack {

public static java.lang.String toString();

public static int hashCode();

public static boolean canEqual(java.lang.Object);

public static scala.collection.Iterator<java.lang.Object> productIterator();

public static java.lang.Object productElement(int);

public static int productArity();

public static java.lang.String productPrefix();

} $ javap -p Slack\$.class

Compiled from "Notifications.scala"

public final class Slack$

implements Notification,scala.Product,scala.Serializable {

public static final Slack$ MODULE$;

public static {};

public java.lang.String productPrefix();

public int productArity();

public java.lang.Object productElement(int);

public scala.collection.Iterator<java.lang.Object> productIterator();

public boolean canEqual(java.lang.Object);

public int hashCode();

public java.lang.String toString();

private java.lang.Object readResolve();

private Slack$();

}

If you took a deeper dive into Slack.class with javap -c (which shows the actual bytecode), you would notice that this class is only a helper whose static methods delegate to the respective ones in Slack$ .

Now in the Slack$ class, at the very end, you can notice the actual private constructor that can’t be used with new (since it’s private), but can still be called using reflection.

The other crucial part of the Slack$ class is the static MODULE$ field — which turns out to be the singleton instance created internally by calling the private constructor.

As long as you access the object in your Scala code using Slack , or in your Java code using Slack$.MODULE$ (which looks ugly but is the way to go), it remains a singleton. However, as you already know, nothing can prevent you from creating another instances using reflection.

Also, if you look deeper into Slack$ with javap -c , you will notice that the MODULE$ static field is initialized every time the private constructor is called:

private Slack$();

Code:

0: aload_0

1: invokespecial #64 // Method java/lang/Object."<init>":()V

4: aload_0

5: putstatic #63 // Field MODULE$:LSlack$;

8: aload_0

9: invokestatic #68 // InterfaceMethod scala/Product.$init$:(Lscala/Product;)V

12: return

This is the reason why only some instances (to be precise — only the most recently created one) are matched and the others are not. Kudos to Kamil Rafałko for pointing this out.

The solution

Coming back to our case with reading the Notification s from MongoDB, it should now be clear why the pattern matching didn’t work: a new instance of Slack was created every time the value was deserialized.

To fix it, we decided to write a custom TypeConverter that Morphia would use when deserializing Slack objects, which would always provide the singleton instance instead of creating a new one every time:

The idea behind the custom converter is to handle the special case of deserializing the Slack value and falling back to default behavior in any other case.

The summary

Although under normal circumstances the object s in Scala are indeed singletons, this can no longer be the case when reflection comes into play. From the reflection perspective, an object is nothing more than any other class with a private constructor.

Moreover, when there’s more than a single instance of an object , their behavior can become pretty weird — check the examples below and see for yourself.

The riddle

Can you tell what would be the result of running the code below? Try it in the Scala REPL or in Ammonite (which is a more powerful REPL with syntax highlighting). Is the output as you expected? Is it deterministic?