Java – Does extending a class which itself extends a class which itself… and so on… is healthy..

design-patternsgeneric-programminggenericsjava

NOTE : Feel free to edit the title if it is somewhat different than my question.

In one of our application, we are maintaining a lot of properties inside Instance object. Obviously it's maintained as Map<K, Map...>. There are few instance objects which holds different types of properties.

For example

1) AppPropertyReader – Map<String, String>

2) SMSPropertyReader – Map<UUID, Map<SMSType, String>> // SMSType is enum

3) PropertyReader – Map<UUID, Map<String, Map<String, String>>>

and a few more…

So for every reader class, I've to define getter(s) and setter. Depending upon the no of nested types, new getters have to be introduced. But I feel that it's somewhat poorer / uglier (something which I can't express clearly) to duplicate the getter & other essential codes in every reader class. So I've written three generic classes for handling upto three maps (like defined in PropertyReader class).

Base Map holder

import java.util.HashMap;
import java.util.Map;

public abstract class CommonPropertyHolder<K, V> {

    protected Map<K, V> properties;

    public CommonPropertyHolder() {
        init();
    }

    protected void init() {
        properties = new HashMap<K, V>();
    }

    public Map<K, V> getProperties() {
        return properties;
    }

    public V getProperty(K key) {
        return properties.get(key);
    }

    protected void setProperty(K key, V value) {
        properties.put(key, value);
    }

    protected void clear() {
        init();
    }

}

Nested Map holder

import java.util.HashMap;
import java.util.Map;

public class CommonMapPropertyHolder<T, K, V> extends CommonPropertyHolder<T, Map<K, V>> {

    public V getProperty(T type, K key) {
        Map<K, V> properties = getProperty(type);
        if( properties == null ) return null;
        return properties.get(key);
    }

    protected void setProperty(T type, K key, V value) {
        Map<K, V> properties = getProperty(type);
        if( properties == null ) {
            properties = new HashMap<K, V>();
            setProperty(type, properties);
        }
        properties.put(key, value);
    }

}

Deep Nested Map holder (not sure about if it can be expressed as "Deep Nested")

import java.util.HashMap;
import java.util.Map;

public class CommonMultiMapPropertyHolder<T, PK, K, V> extends CommonMapPropertyHolder<T, PK, Map<K, V>> {

    public V getProperty(T type, PK parentKey, K key) {
        Map<K, V> properties = getProperty(type, parentKey);
        if( properties == null ) return null;
        return properties.get(key);
    }

    protected void setProperty(T type, PK parentKey, K key, V value) {
        Map<PK, Map<K, V>> mapProperties = getProperty(type);
        if( mapProperties == null ) {
            mapProperties = new HashMap<PK, Map<K, V>>();
            setProperty(type, mapProperties);
        }
        Map<K, V> properties = mapProperties.get(parentKey);
        if( properties == null ) {
            properties = new HashMap<K, V>();
            mapProperties.put(parentKey, properties);
        }
        properties.put(key, value);
    }

}

So my property reader classes can extend like

class AppPropertyReader extends CommonPropertyHolder<String, String>

class SMSPropertyReader extends CommonMapPropertyHolder<UUID, SMSType, String>

class PropertyReader extends CommonMultiMapPropertyHolder<UUID, String, String, String>

This works amazingly perfect.

In normal view, these generic classes won't be much a difference. But if you look at how each class refers super methods and variables, you will know how beautiful it is.

If I ever want to introduce another PropertyReader, I just have to extend the appropriate Generic class without writing any code other than class declaration.

Here are my questions.

1) Since it goes upto 3 levels deeper for most parameterized generics, will it affect the runtime performace..? (I read somewhere in stackoverflow that depth upto 5 levels don't have noticable performance impact.) But, since every extended class is generics, will there be a performance impact..?

2) Before I came up with these 3 generic classes, I tried to achieve any level of nested maps with just a single generic class. But I can't come up with any implementable solution. Is it possible to just implement a single generics class which can handle any levels of depths..? (Ofcourse everything have to be recursion if implementable)

Best Answer

To answer your questions directly:

1) No, it will not affect performance much. Generics are mostly erased by the time the program gets executed anyway, so the additional cost is really only multiple (up to 3) map lookups instead of 1. I would not expect this to ever be a bottleneck in a scenario where usage of a standard HashMap is acceptable.

2) Not really. Generics are used for compile-time safety, and the compiler doesn't really like any unknown thing during compilation, including number of params. I hear C++ templates would do the trick, but that's quite a different beast (although often used for the same thing).


A couple of side comments about your approach:

1) At least the 2-key map is a very common problem, and a solved one. Look up Guava's Table - it's pretty much what you want, a table that has 2 keys, along with some additional bonuses.

2) Unless you need to query per-row or per-column maps separately (rather than just do single property reads or writes), a combined key is preferable when there are multiple dimensions. You can see how your signatures bloat in size. Whereas a simple key class will have it in a much more readable form.

3) If you insist on long generic type lists, then a much better way of doing this would be composition. It's what you're doing anyway, just without the clutter (you have 3 setProperty methods exposed in your "multimap", and if you decide you need a 4th param, the method will just keep on growing). All arguments for the "Composition over inheritance" principle set out by numerous people apply here as well.

4) A Multimap is commonly known as a map of keys to collection of values, rather than map-to-map-to-map-to-values. Might confuse your teammates if you stick with that naming of your class :)

Related Topic