Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inherited some bad Java generics

Tags:

java

generics

This is a nasty problem, and it might be that the design is just bad.

Writing a set of simple charts components (pie, bar & line charts) and am choking on some generics stuff. In advance, I'm sure there are many Java APIs for doing exactly what I'm trying to do here (charting/reports/etc.), however I'm interested in this as a general generics problem; the fact that it involves charts & reporting components is trivial.

Every chart inherits from a generic abstract base class Chart:

public abstract class Chart<T extends ChartComponent>
{
    private List<T> components;

    // ...rest of the Chart class
}

The reason why we have T extends ChartComponent is because every chart subclass will be comprised of 1+ so-called chart components (bars, lines, pie wedges, etc.):

public abstract class ChartComponent
{
    private Color color;

    // .. rest of ChartComponent class
}

public class PieWedge extends ChartComponent
{
    double wedgeValue;

    // ... rest of PieWedge class
}

Putting this design together:

public class PieChart extends Chart<PieWedge>
{
    // ... thus its list of ChartComponents is actually a List<PieWedge>
}

This way, PieChart isn't generic (nor should it be) and is always of type Chart<PieWedge>.

I previously had the same setup for bar and line charts, which were defined as BarChart extends Chart<BarGroup> and LineChart extends Chart<Line> respectively (since a bar chart consists of 1+ groups of bars, and a line chart consists of 1+ lines).

Now I want to abstract Bar and Line charts out even further. Both of these charts are actually plotted against an (x,y) Cartesian graph with x- and y-axes; this is as opposed to a pie chart which is not plotted against any such axes.

Ideally, I wanted to create a new abstract class called CartesianChart which extended Chart, and then have BarChart and LineChart both extend CartesianChart. This new CartesianChart would introduce new properties (xAxisLabel, gridTurnedOn, etc.) that logically apply to bar/line charts but not pie charts.

Furthermore, to restrict CartesianChart so that it could only have chartComponents of type BarGroup or Line (and not PieWedge), I would like to create a new chart component type like CartesianComponent extends ChartComponent, and then have BarGroup/Line extend that. Doing so would prevent code like this from compiling:

LineChart lineChart = new LineChart();
lineChart.addLine(new PieWedge());

Since Line extends CartesianComponent, but PieWedge only extends ChartComponent. Thus, before getting to my problem we have the following inheritance hierarchy:

Chart
    CartesianChart
        BarChart
        LineChart
    PieChart

ChartComponent
    CartesianComponent
        BarGroup
        Line
    PieWedge

PieChart extends Chart<PieWedge>

CartesianChart extends Chart<CartesianComponent>

BarGroup extends CartesianComponent
Line extends CartesianComponent

BarChart extends CartesianChart<BarGroup>
LineChart extends CartesianChart<Line>

The problem with this setup is that on both BarChart and LineChart it gives a compiler error complaining that CartesianChart is not generic. This makes complete sense, but I'm not sure what I can do to fix it!

If I try to re-define CartesianChart:

public abstract class CartesianChart<T extends CartesianComponent> extends Chart<CartesianComponent>
{
    // ...
}

I get "type mismatch" compiler errors all through my bar/line chart code. In every instance of the error, it states that it is expecting arguments of type List<CartesianComponent> but instead found List<BarGroup> or List<Line> and that they are not suitable substitutes.

Hopefully, this is a quick fix somewhere in the class definition of CartesianChart and/or CartesianComponent. Otherwise I may have to re-design the entire chart library. Either way, I'm interested in any and all suggestions, except ones like "Hey, why don't you just try JFreeCharts or ...". Again, I'm interested in the solution here as it related to solving a broad range of similar generics problems; the fact that this involves reporting/charting is trivial.

Thanks in advance for any and all help!

like image 505
IAmYourFaja Avatar asked Nov 07 '11 21:11

IAmYourFaja


People also ask

Can generic types be inherited Java?

Generics also provide type safety (ensuring that an operation is being performed on the right type of data before executing that operation). Hierarchical classifications are allowed by Inheritance. Superclass is a class that is inherited. The subclass is a class that does inherit.

Are generics bad Java?

Many people are unsatisfied with the restrictions caused by the way generics are implemented in Java. Specifically, they are unhappy that generic type parameters are not reified: they are not available at runtime. Generics are implemented using erasure, in which generic type parameters are simply removed at runtime.

Can generic classes be inherited?

Generic classes support an inheritance mechanism and can form hierarchies. Any generic class that takes a type T as a parameter can be inherited by another derived class. In this case, a parameter of type T is passed to the derived class.


1 Answers

Your Chart class contains the List<T> that you speak of, so when you you define your CartesianChart abstract class to extend Chart<CartesianComponent>, you are saying that List<T> is really List<CartesianComponent>.

Really, what you want is to just use the generic as you defined it in your abstract class (that is, <T extends CartesianComponent>). I would try doing this and see how it works out.

public abstract class CartesianChart<T extends CartesianComponent> extends Chart<T>
{
    // ...
}
like image 154
nicholas.hauschild Avatar answered Sep 22 '22 10:09

nicholas.hauschild