This article discusses the limitations and hassles of java generics when dealing with “parallel class hierarchies”. A parallel class hierarchy is the situation where you have a “main” class hierarchy and a “secondary” class hierarchy that mirrors the “main” hierarchy. The secondary class models some (isolatable/groupable) aspect of the main class that can and should be separated into another class. This frequently shows up in classical object or relational data models.
To get concrete, let’s start with two main classes: Equipment
and Structure
. Equipment
is generally mobile, whereas Structure
is not. Both are subclasses of a common abstract class PhysicalItem
. The class diagram and java classes are shown below. Fields are omitted.

public abstract static class PhysicalItem {}
public static class Equipment extends PhysicalItem {}
public static class Structure extends PhysicalItem {}
Now let’s add a “Status” attribute to this hierarchy. A Status is a collection of properties that changes tofether (and frequently), thus we wish to put them into a separate class, rather than the main class.

public abstract static class PhysicalItem {}
public static class Equipment extends PhysicalItem {}
public static class Structure extends PhysicalItem {}
public abstract static class PhysicalItemStatus {}
public static class EquipmentStatus extends PhysicalItemStatus {}
public static class StructureStatus extends PhysicalItemStatus {}
The data modellers draw an arrow (directional or bidirectional) between the PhysicalItem and PhysicalItemStatus, note the need for the subclasses to handle the appropriate class casting, and consider themselves done.
The programmers have multiple ways to implement that arrow. They can add a field named “status” to the PhysicalItem of type PhysicalItemStatus. And/or they can add a field “item” to the PhysicalItemStatus of type PhysicalItem. Those fields can be in the superclass or in the subclasses. Another option is to create a 3rd class to hold the two fields “status” and “item”, but that is hardly ever done.
For the point of this article, we’re going to choose the first option: Add a field named “status” to the PhysicalItem of type PhysicalItemStatus. So we add getter methods to the PhysicalItem class hierarchy:
public abstract static class PhysicalItem {
protected PhysicalItemStatus status;
public PhysicalItemStatus getStatus() {
return status;
}
}
public static class Equipment extends PhysicalItem {
@Override public EquipmentStatus getStatus() {
return (EquipmentStatus) status;
}
}
public static class Structure extends PhysicalItem {
@Override public StructureStatus getStatus() {
return (StructureStatus) status;
}
}
Everything is perfect. Notice how our IDE (integration development environment) is aware of the method overrides and automatically knows the correct type.
public static void showCastingIsAutomatic(PhysicalItem item, Equipment equipment, Structure structure) {
PhysicalItemStatus itemStatus = item.getStatus();
EquipmentStatus equipmentStatus = equipment.getStatus();
StructureStatus structureStatus = structure.getStatus();
}
Unfortunately, that was the last of the good news. Now we have to deal with the setters:
It is convenient to add a setter method to the PhysicalItem. But now the IDE (and java) allows runtime errors:
public abstract static class PhysicalItem {
protected PhysicalItemStatus status;
public void setStatus(PhysicalItemStatus status) {
this.status = status;
}
}
public static void problemsWithSetter(PhysicalItem item, Equipment equipment, Structure structure,
PhysicalItemStatus itemStatus, EquipmentStatus equipmentStatus, StructureStatus structureStatus) {
item.setStatus(itemStatus); //fine
equipment.setStatus(equipmentStatus); //fine
structure.setStatus(structureStatus); //fine
equipment.setStatus(structureStatus); //WRONG TYPE. RUNTIME ERROR
structure.setStatus(equipmentStatus); //WRONG TYPE. RUNTIME ERROR
}
We can add setter methods to the subclass but they cannot OVERRIDE the parent setter method. So client code sees BOTH methods. It is STILL possible to cause a runtime error.
public abstract static class PhysicalItem {
protected PhysicalItemStatus status;
public void setStatus(PhysicalItemStatus status) {
this.status = status;
}
}
public static class Equipment extends PhysicalItem {
public void setStatus(EquipmentStatus status) {
this.status = status;
}
}
public static class Structure extends PhysicalItem {
public void setStatus(StructureStatus status) {
this.status = status;
}
}
The image below is the eclipse IDE showing the suggestions to complete the setter method. the problem is that we see TWO methods. One is right and the other leads to runtime errors.

Before java gained “generics” we had to live with this. The code had to be written defensively to throw errors when the wrong method was called. And the multiple method problem was so annoying we usually just lived with the single method in the superclass.
When java generics arrived, we could convert our Status class into a generic. Then we could declare our getter and setter in the superclass using the generic variable. Our subclasses now require the correct argument types and can show errors at COMPILE TIME.
public abstract static class PhysicalItem<STATUS extends PhysicalItemStatus> {
protected STATUS status;
public STATUS getStatus() {
return status;
}
public void setStatus(STATUS status) {
this.status = status;
}
}
public static class Equipment extends PhysicalItem<EquipmentStatus> {}
public static class Structure extends PhysicalItem<StructureStatus> {}
public static void getterCastingIsAutomatic(PhysicalItem item, Equipment equipment, Structure structure) {
PhysicalItemStatus itemStatus = item.getStatus();
EquipmentStatus equipmentStatus = equipment.getStatus();
StructureStatus structureStatus = structure.getStatus();
}
public static void noProblemsWithSubclassSetters(Equipment equipment, Structure structure,
PhysicalItemStatus itemStatus, EquipmentStatus equipmentStatus, StructureStatus structureStatus) {
equipment.setStatus(equipmentStatus); //fine
structure.setStatus(structureStatus); //fine
equipment.setStatus(structureStatus); //WRONG TYPE. COMPILER ERROR
equipment.setStatus(itemStatus); //WRONG TYPE. COMPILER ERROR
structure.setStatus(equipmentStatus); //WRONG TYPE. COMPILER ERROR
structure.setStatus(itemStatus); //WRONG TYPE. COMPILER ERROR
}
public static void potentialErrorsWithSuperclassSetters(PhysicalItem item, PhysicalItemStatus itemStatus, EquipmentStatus equipmentStatus, StructureStatus structureStatus) {
item.setStatus(itemStatus); //LEGAL BUT POTENTIAL RUNTIME ERROR
item.setStatus(equipmentStatus); //LEGAL BUT POTENTIAL RUNTIME ERROR
item.setStatus(structureStatus); //LEGAL BUT POTENTIAL RUNTIME ERROR
}
For those who complain about CSS in HTML pages, the oldtime joke: “CSS is the worst thing ever! With the exception of not having CSS.” Well, the same thing applies to generics. “Generics is the worst thing ever, except for not having generics.”
(Well-read readers will have immediately noted that I am stealing the phrase from Winston Churchill: “Many forms of Government have been tried, and will be tried in this world of sin and woe. No one pretends that democracy is perfect or all-wise. Indeed it has been said that democracy is the worst form of Government except all those other forms that have been tried from time to time”.)
There isn’t a solution to using generics in this manner. They do not provide a 100% solution.
Perhaps even worse than not solving the problem, generics spread to all client code. Now ALL clients of the hierarchy must deal with generics, even if that particular code has nothing to do with status. Either you write your code to ignore generics (and ignore the compiler warnings), or you add <?>
everywhere pointlessly. It is a REAL burden.
And it is a burden that multiplies. Because big data models have many aspects that we’d like to extract into secondary classes. So there is a code tug towards many generics, and we wind up referring to PhysicalItem<? extends PhysicalItemStatus, ? extends PhysicalItemHistory, ? extends PhysicalItemType, ? extends CivilOrMilitary>
and more. Every time you add a new generic, you break code in potentially hundreds of places.
To be clear, java generics “disappear at the leaf classes”, i.e., those classes that have no subclasses. (If Equipment
was a concrete subclass, code dealing with Equipment
would have no generics.) However, much of our code will be required to reference intermediate classes (classes which have superclasses and subclasses), and that is where the pain is. Equipment
has many subclasses but a lot of code just needs to know it is an Equipment
. Hence, generics-hell.
After many years of fighting this, I have come to believe that the price of generics is too high to include them in our “main” classes. Generics are very useful, but they MUST be contained within the code that needs them, while all other code must be able to work without knowing about them.
My solution is a “bridge” method that converts the main class to a particular generic instance. This method takes the client from the main instance (that has no generics) to the generic secondary instance (that DOES have the generics).
In our example, there is no getter and setter for the status field on PhysicalItem. Instead, a new method toPhysicalItemStatus()
returns a PhysicalItem “bridge” instance which DOES have generics.
The following is a complete example:
public abstract class PhysicalItem{
protected PhysicalItemStatus status;
/**
* Delegate to a "Bridge" class that "casts" to an instance with the correct generics.
*/
public HasPhysicalItemStatus<? extends PhysicalItemStatus> toPhysicalItemStatus() {
return new HasPhysicalItemStatusBridge<>(this, PhysicalItemStatus.class);
}
}
public class Equipment extends PhysicalItem {
@Override public HasPhysicalItemStatus<EquipmentStatus> toPhysicalItemStatus() {
return new HasPhysicalItemStatusBridge<>(this, EquipmentStatus.class);
}
}
public class Structure extends PhysicalItem {
@Override public HasPhysicalItemStatus<StructureStatus> toPhysicalItemStatus() {
return new HasPhysicalItemStatusBridge<>(this, StructureStatus.class);
}
}
public interface HasPhysicalItemStatus<STATUS extends PhysicalItemStatus>{
STATUS getStatus();
void setStatus(STATUS status);
}
/**
* A "bridge" instance that switches to Status with the correct generics.
* This is a new instance, so theoretically it requires heap space. However, everything is final, so
* short-lived instances will not incur instance allocation and garb age collection.
*
* Put this in the same package as the PhysicalItem so it can access a private/protected field (status).
* Alternatively, can live in a different package, and the constructor needs to provide a getter and setter
*/
public final class HasPhysicalItemStatusBridge<STATUS extends PhysicalItemStatus> implements HasPhysicalItemStatus<STATUS>{
private final PhysicalItem item;
private final Class<STATUS> statusClass;
public HasPhysicalItemStatusBridge(PhysicalItem item, Class<STATUS> statusClass) {
this.statusClass = statusClass;
this.item = item;
}
@SuppressWarnings("unchecked") @Override public STATUS getStatus() {
return (STATUS) item.status;
}
@Override public void setStatus(STATUS newStatus) {
assert statusClass.isInstance(newStatus); //intermediate classes need checking
item.status = newStatus;
}
}
public abstract class PhysicalItemStatus {}
public class EquipmentStatus extends PhysicalItemStatus {}
public class StructureStatus extends PhysicalItemStatus {}
The test code below shows the results of all the getters and setters. Subclasses have correct typing. Intermediate classes have “as correct as can be” methods but will always require some amount of runtime type checking. The possibility of runtime errors exist, but no worse than before. We catch them at runtime and throw an Exception rather than corrupting the data model.
public static void testExamples(PhysicalItem item, Equipment equipment, Structure structure,
PhysicalItemStatus itemStatus, EquipmentStatus equipmentStatus, StructureStatus structureStatus) {
//Correctly typed
HasPhysicalItemStatus<? extends PhysicalItemStatus> physicalItemBridge = item.toPhysicalItemStatus();
HasPhysicalItemStatus<EquipmentStatus> equipmentBridge = equipment.toPhysicalItemStatus();
HasPhysicalItemStatus<StructureStatus> structureBridge = structure.toPhysicalItemStatus();
//Correctly typed
PhysicalItemStatus status1 = physicalItemBridge.getStatus();
EquipmentStatus status2 = equipmentBridge.getStatus();
StructureStatus status3 = structureBridge.getStatus();
//supertype has issues. (But always will)
physicalItemBridge.setStatus(itemStatus); //COMPILE ERROR because the type is <? extends PhysicalItemStatus> instead of <PhysicalItemStatus>
//supertype "solution" to the above error also has issues
HasPhysicalItemStatus<PhysicalItemStatus> physicalItemStatusBridge2 = (HasPhysicalItemStatus<PhysicalItemStatus>) item.toPhysicalItemStatus(); //have to cast
physicalItemStatusBridge2.setStatus(itemStatus); //This is now legal but a possible runtime error, so it needs runtime checking
physicalItemBridge.setStatus(equipmentStatus); //COMPILE ERROR CORRECTLY
physicalItemBridge.setStatus(structureStatus); //COMPILE ERROR CORRECTLY
equipmentBridge.setStatus(itemStatus); //COMPILE ERROR CORRECTLY
equipmentBridge.setStatus(equipmentStatus); //fine
equipmentBridge.setStatus(structureStatus); //COMPILE ERROR CORRECTLY
structureBridge.setStatus(itemStatus); //COMPILE ERROR CORRECTLY
structureBridge.setStatus(equipmentStatus); //COMPILE ERROR CORRECTLY
structureBridge.setStatus(structureStatus); //fine
}
The important point is that:
- The main classes are declared free of generics. So we don’t degrade the overall code base.
- Generics ARE provided for code that can benefit from it. The cost is one extra method that delegates/bridges to an intermediate generically-typed interface..