3

Realm on Android doesn't support model inheritance/polymorphism.

So is there any way to share fields on Android? We have 5 models that all share the same synchronization-related fields and code. We use inheritance in our current SQLite models; if we switch to Realm, is our only choice to duplicate the sync fields across each of the 5 model classes?

As a workaround, I'm thinking about having those classes implement a Syncable interface with getters and setters for the shared fields, which at least let me share sync functionality. Is there a better way?

To share sync functionality, my best guess is to make a static Synchronizer class and pass it Syncable model objects. Synchronizer methods will use the Syncable interface to operate directly on model objects' shared, sync-related fields and delegate operations on type-specific fields. Alternatively, I could provide each model object its own Synchronizer instance...

Trying to find the right way to work around the inheritance limitation is really stretching my OOP skills... help is appreciated!

Nolan Amy
  • 10,785
  • 5
  • 34
  • 45
  • Something to consider- if it doesn't support a feature you need, maybe you should use another library that does? – Gabe Sechan Jul 08 '15 at 00:43
  • You could use composition over inheritance as suggested by the Gang of Four (Design Patterns) and Joshua Bloch (Effective Java). As a side note, we are considering allowing inheritance, but it could only be used during object modelling and not while querying, which would make it feel crippled. What's your opinion about that? – Emanuelez Jul 08 '15 at 08:03
  • @Emanuelez I've had trouble figuring out how composition would help here... are you suggesting that I split each object across two records in Realm – one for shared fields and another for type-specific fields? As I understand the query limitations, it does sound like it would be crippling. Querying abstract models works on iOS, right? What's the issue on Android? – Nolan Amy Jul 08 '15 at 18:20
  • No it does not work on iOS. They face the same limitations. – Emanuelez Jul 09 '15 at 08:24
  • @Emanuelez Huh. Maybe I don't understand what the query issues are... we're using model inheritance on iOS and it works as expected. Is the limitation that queries on shared fields can't return a mix of inherited types? If so, that's totally fine for our purposes: we almost always deal with one inherited type at a time; when we do need to apply an operation to all inherited types, we do a separate query for each type. Sure, it's ugly to maintain a list of the inherited types, but in practice, that's a small price to pay for having shared fields and functionality... – Nolan Amy Jul 09 '15 at 18:59
  • Yes iOS supports model inheritance, but it's not allowed to perform queries that span across inheritance. With composition you can achieve the same result while maintaining full composability and decent query support (especially as soon as we support back-links) – Emanuelez Jul 11 '15 at 17:50
  • @Emanuelez could you elaborate? How do you suggest using composition here? Using a wrapper class for each `RealmObject` subclass, such that the wrapper has-an instance of its corresponding subclass? Splitting classes with shared fields across multiple `RealmObject` subclasses [as suggested by @AliSoftware on github](https://github.com/realm/realm-java/issues/761#issuecomment-119559100) ("Adding a relationship from the Dog and Cat RLMObjects to the Animal RLMObject (composition instead of inheritance)")? Something else? – Nolan Amy Jul 11 '15 at 23:14
  • I mean something in the lines of: `class Animal: int numberOfLegs` and `class Dog: String name, Animal animal` – Emanuelez Jul 13 '15 at 11:41
  • @Emanuelez Okay, thanks. So if this were a SQL database, there'd be a `Dog` table, a `Sparrow` table, and an `Animal` table. Each `Dog` has an `Animal`, and each `Sparrow` has an `Animal`. The application would have to make sure to give each `Dog` or `Sparrow` created its own `Animal`, and to remove the corresponding `Animal` whenever a `Dog` or `Sparrow` is removed. Is that right? – Nolan Amy Jul 13 '15 at 17:24
  • Yes, that is correct. – Emanuelez Jul 13 '15 at 17:28

2 Answers2

2

I had the same issue when I found out that realmObjects should inherit directly form RealmObject class (no support for inheritance). In order to get back the benefits of polymorphism, I considered a similar solution to yours combined with some composition tricks that would avoid me attribute duplication.

"Talk is cheap show me the code."

Code examples

interface IPerson {
    String getName();
}

class Person extends RealmObject implements IPerson {

    String name;

    @Override
    public String getName() {
        return name;
    }
}

interface IWorker extends IPerson {
    int getSalary();
}

class Worker extends RealmObject implements IWorker {

    Person person;
    int salary;

    @Override
    public String getName() {
        return person.getName();
    }

    @Override
    public int getSalary() {
        return salary;
    }
}

Some benefits

You won't have to duplicate your attributes in each extending class.

Polymorphism is back! For example, now you can simulate a cast (with getPerson() in this example).

Some limits

When using a serialization library that uses reflection (suppose it's Gson), your serialized models will have their parents attributes embedded. Not something that you would have had if you were using classic inheritance.

Example with JSON

Let's suppose John Doe is making 500$ a month. (He's a Worker and a Person right?).

With classic inheritance, John Doe would look like this:

{
  "name":"John Doe",
  "salary": 500
}

But with this inheritance workaround ... :

{
  "person" : {
  "name":"John Doe"
  },
  "salary": 500
}

Hope this helps!

Note

PrimaryKeys unfortunately have to be duplicated.

Bonus

You might want to check RealmFieldNamesHelper, a library made by Christian Melchior "to make Realm queries more type safe".

  • how would I store this object in another realm object? say I have: Interface Animal. class Dog:Animal. class Cat:Animal. and I have a realm object Human that has a pet object, can I do this: Human-> (has-a) pet:Animal ? – mhashim6 Sep 01 '18 at 17:07
  • @UpsideDownTree inheritance is something that is not native to Realm unfortunately. My workaround would be to add a member class RealmObject pet, and do run time type checking on the setter/constructor of this attribute (making sure it's an instance of the Dog class). – Anis LOUNIS aka AnixPasBesoin Sep 02 '18 at 09:48
1

If you use Kotlin, sharing the fields via an interface becomes even more trivial:

interface PersonBase {
    var name: String?
    var salary: Int
}

Then

class Person: RealmObject(), PersonBase {
}
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428