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!
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.
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.
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.
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>
{
// ...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With