Java – Designing generic type inheritance hierarchy

designdesign-patternsgenericsinheritancejava

I have now put another rephrased version of original question as requested by the user in the comments with class names mimicking real world scenario of postal office (though I dont know how real world the rephrased example is). Original question had abstract/generalized class names. Also uploaded the correct code for both.


Rephrased Question

Design Requirements

  • Postage and PostageLot: We run a postal office. So we have class Postage and interface PostageLot.
  • Classifying postage lot based on required speed of delivery: There is class SpeedPostageLot and class NormalPostageLot that implement interface PostageLot.
  • Classifying postage lot based on care to be taken:
    • There are interface ExecutivePostageLot and interface PublicPostageLot. ExecutivePostageLot is given extra care than PublicPostageLot. For illustration in code, I considered only ExecutivePostageLot and did not wrote PublicPostageLot, so we will not write corresponding sub-classes for PublicPostageLot
    • Both executive and public postages can be speed or normal postage. Thus I created subclasses of SpeedPostageLot and NormalPostageLot that implement ExecutivePostageLot: ExecutiveSpeedPostageLot and ExecutiveNotmalPostageLot. (Same in case for public postages: PublicSpeedPostageLot and PublicNormalPostageLot)
    • There is also ExecutivePostage class
  • There is also CommonSpeedPostageProcessor which contains common logic to process SpeedPostageLot which can be used by both ExecutivePostageProcessor and PublicPostageProcessor when they deal with SpeedPostageLot. The methods of CommonSpeedPostageLotProcessor should return values of type which can be cast to ExecutiveSpeedPostageLot and PublicSpeedPostageLot. I felt this should be PostageLot or SpeedPostageLot since it does not have any connotation of care-to-be taken (executive or public) attached to it. But this is not working as can be seen in below code.

enter image description here

Code

public class Postage {}
public interface PostageLot<T extends Postage> {}
public abstract class NormalPostageLot<T extends Postage> implements PostageLot<T>{}
public abstract class SpeedPostageLot<T extends Postage> implements PostageLot<T>{}
public interface ExecutivePostageLot extends PostageLot<ExecutivePostage> {}
public class ExecutiveSpeedPostageLot extends SpeedPostageLot<ExecutivePostage> implements ExecutivePostageLot {}
public class ExecutiveNormalPostageLot extends NormalPostageLot<ExecutivePostage> implements ExecutivePostageLot {}
public class ExecutivePostage extends Postage { }

public class CommonSpeedPostageProcessor 
{
    PostageLot<Postage> speedPostageProcessor1()
    {
        SpeedPostageLot<Postage> obj = null;
        return obj;
    }

    SpeedPostageLot<Postage> speedPostageProcessor2()
    {
        SpeedPostageLot<Postage> obj = null;
        return obj;
    }
}

public class ExecutivePostageLotHandler {

    public static void main(String[] args) {
        CommonSpeedPostageProcessor logic = new CommonSpeedPostageProcessor();
        ExecutivePostageLot obj = logic.method1EntitySetProcessor1(); //Type mismatch: cannot convert from PostageLot<Postage> to ExecutivePostageLot
        ExecutivePostageLot obj2 = logic.method1EntitySetProcessor2(); //Type mismatch: cannot convert from SpeedPostage<Postage> to ExecutivePostageLot
        ExecutiveSpeedPostageLot obj3 = logic.method1EntitySetProcessor1(); //Type mismatch: cannot convert from PostageLot<Postage> to ExecutiveSpeedPostageLot
        ExecutiveSpeedPostageLot obj4 = logic.method1EntitySetProcessor2(); //Type mismatch: cannot convert from SpeedPostage<Postage> to ExecutiveSpeedPostageLot
    }
}

(The zip of the eclipse project can be found here)

I understand those typecasts are invalid, but then where in design I made mistake? Primarily I feel that I am doing it right, its just that CommonSpeedPostageProcessor needs to be parameterized with generic type parameter. But I am not able to guess exactly how do I do it. How should I generic-type-parameterize CommonSpeedPostageProcessor?
Or I am simply over complicating the things?


Orignal Question

I am stuck while designing a class hierarchy for our application. Am not able to find any solution for this design constraints scenario.

Design requirements

(Names of classes and interfaces are over generalized to avoid unnecessary confusion due to domain specific names).

  • Entity and EntitySet: We have organization wide Entity class and an interface identifying set of entities EntitySet which enforces basic methods needed to deal with specific type of entity we deal with in our organization.
  • Method specific entity sets (Method1EntitySet and Method2EntitySet): Now there are two main methods to process this entity set. Each method defines class that implements EntitySet: Method1EntitySet and Method2EntitySet.
  • Module specific interfaces and classes:
    • Now we have different modules that are developed by different developer groups. All these modules need to use both Method1EntitySet and Method2EntitySet, however they also want these classes to have some common module specific methods. So they define interface containing module specific methods, say Module1EntitySet and
    • Each module also defines another pair of classes Module1Method1EntitySet and Module1Method2EntitySet, both extending corresponding method specific classes (i.e. Method1EntitySet and Method2EntitySet respectively) and implementing module specific interface (i.e. Module1EntitySet).
    • Also each module defines its own entity Module1Entity
  • The common logic: I want to have a class (CommonMethod1EntitySetLogic) which will contain common functionality (to be used by all modules) of processing Method1EntitySet. All modules dealing with Method1EntitySet will use this common class. Thus the methods in CommonMethod1EntitySetLogic class should return value of type which can be cast to ModuleXMethod1EntitySet or ModuleXEntitySet for any ModuleX from which the mothods are called. I felt this type of the returned values can be EntitySet or Method1EntitySet as they are not module specific. But this is not working as can be seen in below code.

Note: In all above class names, I have omitted the generics type specification to not cause confusion about my design requirements. But this might have lead to incorrect question. So I am providing the code below.

enter image description here

Code

public class Entity { }
public interface EntitySet<T extends Entity> { }
public abstract class Method1EntitySet<T extends Entity> implements EntitySet<T>{}
public abstract class Method2EntitySet<T extends Entity> implements EntitySet<T>{}
public class Module1Entity extends Entity{}
public interface Module1EntitySet extends EntitySet<Module1Entity>{}
public class Module1Method1EntitySet extends Method1EntitySet<Module1Entity> implements Module1EntitySet{}
public class Module1Method2EntitySet extends Method2EntitySet<Module1Entity> implements Module1EntitySet{}

public class CommonMethod1EntitySetLogic 
{
    EntitySet<Entity> method1EntitySetProcessor1()
    {
        Method1EntitySet<Entity> obj = null;
        return obj;
    }

    Method1EntitySet<Entity> method1EntitySetProcessor2()
    {
        Method1EntitySet<Entity> obj = null;
        return obj;
    }
}

Now that all main classes given above, below is where I am getting compile time errors as specified in comments:

public class Module1 {
    public static void main(String[] args) {
        CommonMethod1EntitySetLogic logic = new CommonMethod1EntitySetLogic();
        Module1EntitySet obj = logic.method1EntitySetProcessor1(); //Type mismatch: cannot convert from EntitySet<Entity> to Module1EntitySet
        Module1EntitySet obj2 = logic.method1EntitySetProcessor2(); //Type mismatch: cannot convert from Method1EntitySet<Entity> to Module1EntitySet
        Module1Method1EntitySet obj3 = logic.method1EntitySetProcessor1(); //Type mismatch: cannot convert from EntitySet<Entity> to Module1Method1EntitySet
        Module1Method1EntitySet obj4 = logic.method1EntitySetProcessor2(); //Type mismatch: cannot convert from Method1EntitySet<Entity> to Module1Method1EntitySet
    }
}

I understand those typecasts are invalid, but then where in design I made mistake? Primarily I feel that I am doing it right, its just that CommonMethod1EntitySetLogic needs to be parameterized with generic type parameter. But I am not able to guess exactly how do I do it. How should I generic-type-parameterize CommonMethod1EntitySetLogic?
Or I am simply over complicating the things?

(The zip of the eclipse project can be found here.)

Best Answer

Type System Abuse?

Reading the post office example, there is some evidence to suggest that you might be trying to abuse the type system to encode property values in interfaces. And failing...

The way you are currently implementing it, Speed vs. Normal are mutually exclusive, while there is no reason why a class cannot simultaneously be an ExecutivePostageLot and a PublicPostageLot, as far as I can tell.

From your description, however, it appears that you want to categorize postage lots (and postages for that matter) along two dimensions: velocity (speed vs. normal) and importance (executive vs. public).

If so, having an interface for each of the values is not how you should approach it.

Alternative Interpretation

But your question and the diagrams seem to allow another way to interpret what you're trying to do. Maybe I'm imagining things, but I think you want to do the following (I'll use a different example, but it should map 1:1 to your diagram):

You have an Entity class with common methods - e.g. int Id() and DateTime Created().

You have multiple modules, each module defining its own Entity subclasses. So for example, you have a purchasing module (with entities like Order and Product), and a newsletter module (with entities like Post and Subscription).

Now, it seems you want to work with various kinds of sets of a particular subclass of Entity. Some of those sets need to provide only generic functionality that is not specific to a kind of entity. For example, there might be a SortableSet like the following:

class SortableSet<T extends Entity> 
    implements EntitySet<T>
{
    // resorts the set (i know, this makes no sense for a set).
    void Sort(IComparer<T> cmp);

    // ... whatever methods are required by EntitySet ...
    // ... these might also be abstract, of course ...
}

Each of the modules, however, may also need more specific functionality added to our sets. The newsletter module might need to combine all posts in a set to form a new super-post:

// assuming Post has subclasses, otherwise this does not
// need to be generic, of course.
interface CombinablePostSet<T extends Post>
    implements EntitySet<T>
{
    T combineAll();
}

Now we can define a class that combines the required functionality of a module with the generic functionality provided by a general purpose EntitySet, like SortableSet.

class CombinableSortablePostSet<T extends Post>
    extends SortableSet<T>
    implements CombinablePostSet<T>
{
    T combineAll() { ... }
}

If, for some reason, we want to create a class that produces a SortableSet, it should probably not create a set of basic entities, but of a specific entity type. This means we have to let the method know the type of entity that will be in the set. This can be solved with a generic method. (Or with a generic class, but let's go with the method for now).

Of course, in the method body, you can still only treat the elements as Entities, not as Products or Posts, because this method does not know anything about their module-specific functionality.

// this might be a Factory?
class CommonSortableSetLogic
{
    <T extends Entity> SortableSet<T> Processor()
    {
        SortableSet<T> set = null;
        return set;
    }
}

If instead the method returned a SortableSet<Entity> or even an EntitySet<Entity>, we cannot simply cast the returned result to just any subclass of EntitySet with any generic parameter, just because that is the type we need. This is the reason you're getting errors - your CommonMethod1EntitySetLogic methods need to provide the right kind of EntitySet.

But, factories and the like aside, the common logic for a SortableSet<T> should be implemented in the SortableSet<T> class itself. That's why its a class (from which you derive) and not an interface.

Some Notes

Assuming I have understood what you wanted to do, there's still the question of whether you should actually do it like this. We would need to know more about your actual application (what's the purpose of the Entity class - what's special about entity sets that is different from regular sets, etc).

With proper use of generics, operation classes that work on regular sets (like a SetSorter<T>) might lead to less coupling and a clearer structure, if composed well. Anyways, this is a topic for another day maybe.

If you do stick to the design, just remember that A<X> cannot be cast to B<X> or A<Y>, even if B extends A and Y extends X. This kind of thing tends to bite you when not being careful with generics.

Related Topic