【问题标题】:Inherited some bad Java generics继承了一些不好的 Java 泛型
【发布时间】:2011-11-07 21:28:17
【问题描述】:

这是一个令人讨厌的问题,可能是设计很糟糕。

编写一组简单的图表组件(饼图、条形图和折线图),并且对一些泛型的东西感到窒息。在此之前,我确信有很多 Java API 可以完全按照我在这里尝试做的事情(图表/报告/等),但是我对此感兴趣,因为这是一个通用的泛型问题;它涉及图表和报告组件的事实是微不足道的。

每个图表都继承自一个通用抽象基类Chart

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

    // ...rest of the Chart class
}

我们之所以有T extends ChartComponent 是因为每个图表子类都将由 1+ 个所谓的图表组件(条形、线条、饼形楔形等)组成:

public abstract class ChartComponent
{
    private Color color;

    // .. rest of ChartComponent class
}

public class PieWedge extends ChartComponent
{
    double wedgeValue;

    // ... rest of PieWedge class
}

把这个设计放在一起:

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

这样,PieChart 不是泛型的(也不应该是泛型的)并且始终是Chart&lt;PieWedge&gt; 类型。

我之前对条形图和折线图有相同的设置,分别定义为 BarChart extends Chart&lt;BarGroup&gt;LineChart extends Chart&lt;Line&gt;(因为条形图由 1+ 组条组成,而折线图由 1+ 条线组成) .

现在我想进一步抽象条形图和折线图。这两个图表实际上都是针对具有 x 轴和 y 轴的 (x,y) 笛卡尔图绘制的;这与不针对任何此类轴绘制的饼图相反。

理想情况下,我想创建一个名为CartesianChart 的新抽象类,它扩展Chart,然后让BarChartLineChart 都扩展CartesianChart。这个新的CartesianChart 将引入新的属性(xAxisLabelgridTurnedOn 等),这些属性在逻辑上适用于条形图/折线图,但不适用于饼图。

此外,为了限制CartesianChart,使其只能具有chartComponents 类型为BarGroupLine(而不是PieWedge),我想创建一个新的图表组件类型,如CartesianComponent extends ChartComponent ,然后让BarGroup/Line 扩展它。这样做会阻止这样的代码编译:

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

由于Line 扩展了CartesianComponent,但PieWedge 仅扩展了ChartComponent。因此,在解决我的问题之前,我们有以下继承层次结构:

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>

此设置的问题在于,在BarChartLineChart 上都会出现编译器错误,抱怨CartesianChart 不是通用的。 这完全有道理,但我不确定我能做些什么来解决它!

如果我尝试重新定义CartesianChart

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

我的条形图/折线图代码都出现“类型不匹配”编译器错误。在每个错误实例中,它都声明它需要 List&lt;CartesianComponent&gt; 类型的参数,但发现 List&lt;BarGroup&gt;List&lt;Line&gt; 并且它们不是合适的替代品。

希望这是CartesianChart 和/或CartesianComponent 的类定义中某处的快速修复。否则我可能不得不重新设计整个图表库。无论哪种方式,我都对任何和所有建议都感兴趣,除了诸如“嘿,你为什么不试试 JFreeCharts 或 ...”之类的建议。同样,我对这里的解决方案很感兴趣,因为它与解决广泛的类似泛型问题有关;这涉及报告/图表的事实是微不足道的。

提前感谢您的任何帮助!

【问题讨论】:

  • 你想通过使Chart 通用来实现什么?换句话说,为什么不直接定义class Chart { private List&lt;ChartComponent&gt; components; }
  • public abstract class CartesianChart&lt;T extends CartesianComponent&gt; extends Chart&lt;T&gt;?
  • 感谢您的建议。我会在这里尝试你的两个建议。请参阅我在@nicholas.hauschild 下方的评论,我担心扩展 Chart 不会阻止子类在 T 不扩展 ChartComponent 的地方创建 Chart。

标签: java generics


【解决方案1】:

您的Chart 类包含您所说的List&lt;T&gt;,因此当您定义CartesianChart 抽象类以扩展Chart&lt;CartesianComponent&gt; 时,您是在说List&lt;T&gt; 真的是List&lt;CartesianComponent&gt;

真的,您想要的只是使用您在抽象类中定义的泛型(即&lt;T extends CartesianComponent&gt;)。我会尝试这样做,看看效果如何。

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

【讨论】:

  • 这是一个非常好的建议(谢谢!),但是我担心这会允许像这样定义子类:public class Diagram extends Chart,其中 Widget 不是ChartComponent 子类。重要的是所有类型“T”都(在某些时候)从 ChartComponent 继承。
  • 实际上不会,因为您已经在此范围内定义了T,以限制为CartesianComponent(根据CartesianChart&lt;T extends CartesianComponent&gt;)。试试我的建议,然后尝试在其中添加Widget...
  • 太棒了,太棒了,太棒了。再次感谢您!
【解决方案2】:

使用接口。

public interface IsAPieChart {

}

public interface IsACartesianChart {

}

他们甚至不需要任何方法。

您的 addLine() 方法配置文件将显示为:

public void addLine(IsACartesianChart cartesianChart);

你的抽象类会这样写:

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

并使用 IsACartesianChart 以同样的方式标记 CartesianChart。现在 addLine() 不会接受 PieChart 的任何内容,因为没有 PieChart 实现 IsACartesianChart 接口,但它会接受 CartesianChart 子类的任何内容,因为所有子类都实现 IsACartesianChart。

使用这样的接口是重新引入在一堆类追溯到同一个超类时丢失的区别的好方法。超类和子类形成严格的层次结构,而接口可以在任何需要的地方附加。

【讨论】:

    【解决方案3】:

    之所以有T extends ChartComponent,是因为每个图表 子类将由 1+ 个所谓的图表组件(条形图、 线,饼楔等):

    这是你的红鲱鱼,这里不需要使用泛型。这是Composition 问题,而不是Generics 问题。

    让你的列表看起来像:

    private List<ChartComponent> components;
    

    这就是您需要的所有类型安全性。

    【讨论】:

    • 谢谢!如果有机会,我将在本周晚些时候尝试这个建议。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-06
    • 1970-01-01
    • 2021-10-11
    • 2011-04-11
    • 2011-04-25
    相关资源
    最近更新 更多