Death by a (Thousand) casts

David East
Google Developers
Published in
2 min readJan 21, 2016

--

When everything is an Object, nothing is.

Firebase event listeners are Magic. As data is updated in Firebase, notifications are pushed to your Android app in the form of handy JSON encoded map data. While it’s nice to have all the plumbing wrapped up, if you’re not careful using this map can cause maintenance nightmares in your code.

The Map is a trap

The information you receive in onDataChanged is in the form a JSON encoded Map object. Of course, you’ll want to extract information FROM that Map, which means you’ll most likely want to typecast the objects from their map data type to some usable object.

Consider this common example below. While it may seem straightforward, don’t trust it. It’s a trap.

// WARNING: This is an anti-pattern, copy and paste at your own risk
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
Object data = dataSnapshot.getValue();
Map<String, Object> map = (Map<String, Object>)data;
String name = (String) map.get("name");
String location = (String) map.get("location");
}

The problem is that using a map object results in lots of object casting operations.

This type of code-pattern lends yourself to all sorts of troublesome maintenance problems.

Thankfully Firebase has a handy process that allows you to avoid both of these problems. It’s called the .class file.

Deserialize with .class

The .class pattern allows you to define classes which represent your data structure, so that you can extract them during your deserialization process faster.

To use this, we first need to create our class definition for our Person.

public class Person {
String name;
String location;
// getters required for deserialization
public String getName() { return name; }
public String getLocation() { return location; }
}

When give a a DataSnapshot in onDataChange, you can use the .getValue() method, passing in the .class of the person. When you do this, Firebase will use reflection to deserialize the JSON content, and populate the properties of the instantiated Person object on your behalf.

@Override
public void onDataChange(DataSnapshot dataSnapshot) {
Person person = dataSnapshot.getValue(Person.class);
}

This one-liner works because the Firebase SDK is bundled with Jackson for deserialization.

The result is no more multiline type-casting, and you don’t have to worry about handling a ClassCastException. If the property doesn’t exist in the DataSnapshot value, it the property will initialize to its default value.

Don’t go it alone

When it comes to deserializing your data, don’t bog yourself down with a never ending chain of typecasts. Let Firebase do the heavy lifting for you.

--

--

David East
Google Developers

Developer Advocate at Google. Working on the Firebases.