Two related hierarchies of classes In order to do its work, a DataProcessor needs Data and otherData.

One instance of Data will be shared over a set of DataProcessor,
but each DataProcessor has its own otherData.

For sharing Data instances, fields of Data could not be defined inside DataProcessor,
thus, there are two hierarchies: Data and DataProcessor.

Each subClass of DataProcessor relates a subClass of Data.

And I have tried three design to fit the requirement, which way is the best? why?

Is my design bad? Why?

Is there any other solution?

Is there any existing design pattern to solve this kind of problem?

It is said that this is a "parallel inheritance hierarchy". Should I avoid this? How? Or is it a good design for my requirement, and do I not need to avoid?

Data hierarchies

abstract class Data {
    int field1;
}

abstract class AData extends Data {
    int field2;
}

class AAData extends AData {
    int field3;
}

class BData extends Data {
    int field4;
}


DataProcessor hierarchies way 1, each super class instance keeps a private reference to Data

abstract class DataProcessor {
    private Data data;
    protected Object otherData;
    DataProcessor(Data data){
        this.data = data;
    }
    void doSomethingUsingDataAndOtherData(){}
}

abstract class ADataProcessor extends DataProcessor {
    private AData data;
    ADataProcessor(AData data) {
        super(data);
        this.data = data;
    }
    void doSomethingUsingADataAndOtherData(){}
}

class AADataProcessor extends ADataProcessor {
    private AAData data;
    AADataProcessor(AAData data) {
        super(data);
        this.data = data;
    }
    void doSomethingUsingAADataAndOtherData(){}
}

class BDataProcessor extends DataProcessor {
    private BData data;
    BDataProcessor(BData data) {
        super(data);
        this.data = data;
    }
    void doSomethingUsingBDataAndOtherData(){}
}


DataProcessor hierarchies way 2, only top superclass instance keeps a protected reference to Data, when subclass needs to use the data, it has to cast the data to its expected Data subclass. And I don't want to define DataProcessor as DataProcessor, because once introducing generic types, I have to use generic types everywhere, and sometimes using generic types is not so simple (i.e. we can not create a generic array in java).

abstract class DataProcessor {
    protected Data data;
    protected Object otherData;
    DataProcessor(Data data) {
        this.data = data;
    }
    void doSomethingUsingDataAndOtherData(){}
}

abstract class ADataProcessor extends DataProcessor {
    ADataProcessor(AData data) {
        super(data);
    }
    void doSomethingUsingADataAndOtherData(){}
}

class AADataProcessor extends ADataProcessor {
    AADataProcessor(AAData data) {
        super(data);
    }
    void doSomethingUsingAADataAndOtherData(){}
}

class BDataProcessor extends DataProcessor {
    BDataProcessor(BData data) {
        super(data);
    }
    void doSomethingUsingBDataAndOtherData(){}
}


DataProcessor hierarchies way3, only bottom sub class instance keep a priavte reference to Data, super class use a abstract method getData() to retrieve the Data

abstract class DataProcessor {
    protected Object otherData;
    protected abstract Data getData();
    void doSomethingUsingDataAndOtherData(){}
}

abstract class ADataProcessor extends DataProcessor {
    protected abstract AData getData();
    void doSomethingUsingADataAndOtherData(){}
}

class AADataProcessor extends ADataProcessor {
    private AAData data;
    protected AAData getData() {
        return data;
    }
    AADataProcessor(AAData data) {
        this.data = data;
    }
    void doSomethingUsingAADataAndOtherData(){}
}

class BDataProcessor extends DataProcessor {
    private BData data;
    protected BData getData() {
        return data;
    }
    BDataProcessor(BData data) {
        this.data = data;
    }
    void doSomethingUsingBDataAndOtherData(){}
}
Well, given the examples, i'd avoid inheritance and implement an interface for the DataProcessor.

package net.icodeapps.examples.ooad;

public interface DataProcessor&lt;T, OtherData&gt; {

    T processData();
}


This could be implement by any class like

package net.icodeapps.examples.ooad;

public class ADataProcessor implements DataProcessor&lt;AData, OtherData&gt; {

    private AData aData;

    private OtherData otherData;

    public ADataProcessor(AData aData, OtherData otherData) {
        this.aData = aData;
        this.otherData = otherData;
    }

    @Override
    public AData processData() {
        return aData;
    }
}


Having a second class implementing the interface

package net.icodeapps.examples.ooad;

public class BDataProcessor implements DataProcessor&lt;BData&gt; {

    private BData aData;

    private OtherData otherData;

    public BDataProcessor(BData aData, OtherData otherData) {
        this.aData = aData;
        this.otherData = otherData;
    }

    @Override
    public BData processData() {
        return aData;
    }
}


With more information, still would skip the abstract class. The reason you use it is providing a contract and avoid code replication. But since the code you try to avoid replicating is the problem itself, i'd skip that and just provide private fields to store references. That way you have the right type in processData.

Keeping the processors stateless and just store the data in a queue could be a better approach though. Depending on the data, you would create a processor on demand. The could be implemented via the command processor pattern. Using that pattern, more complexity would be introduced though.