我有一个类InteractiveChart
,它封装了Chart
(接口(,并通过将用户交互映射到Chart
上的setter调用来为其添加交互性。
1( 因为Chart
的不同子类可能具有不同的"额外"功能,所以我希望在包装类中有一个getter来返回包装后的Chart
(无需执行未检查的强制转换!(。我知道的唯一方法是在包装类上有一个泛型参数。
2( 这个包装类也用于NetBeans的GUI Builder,它具有需要一个空构造函数的约束。然而,泛型参数的运行时类型是由实例化它的代码决定的,我无法控制NetBeans如何实例化它。在这种情况下,我希望它包装一个SimpleChart
,如果用户想要包装其他东西,他们必须在GUI中添加一个JPanel
占位符,并在用户代码中添加图表。由于相同的约束,我不能在包装类上有一个泛型参数。
public class InteractiveChart<C extends Chart> extends JPanel {
private final C wrappedChart;
public InteractiveChart() {
// Compiler error: at this point C can be any other subclass of Chart
this(new SimpleChart());
}
public InteractiveChart(C chart) { wrappedChart = chart; }
public C getWrappedChart() { return wrappedChart; }
}
如何解决1和2之间的难题?
我目前最好的解决方案是创建一个名为InteractiveSimpleChart
的InteractiveChart
子类,用于固定泛型参数。我希望找到一种方法来消除这个子类,因为每次我添加一种新的Chart时,我也必须添加一个新的包装器来进行交互。
我可能没有很好地解释这一点,但类型C
一次只能是单个类型。当你说C extends Chart
时,你限制了C的类型,但归根结底,它只能是这些类型中的一种。换句话说,任何给定的交互图表都可能是Chart类型的交互图表或SimpleChart类型,但不能同时是两者。
因此,正如它所写的,您的第二个构造函数必须采用完全相同的类型。C extends Chart
不是指"任何扩展Chart的类"。它的意思是"一个特定的类,它恰好扩展了Chart"。出现编译错误是因为构造函数需要一个C类型的类,但您试图为它提供一个SimpleChart类型的类。C可能不是SimpleChart。例如,它可能是一个图表,也可能是其他图表。
TL;DR:为了在第二个构造函数中要求"任何扩展Chart的类",只需使用Chart
而不是C
。:(
public InteractiveChart(Chart chart) { .. }
问题解决了。
当然,这带来了一个更好的问题,那就是你真的需要在这里使用泛型吗?到目前为止,你还没有展示任何需要它的东西。
所以,据我所知,您的需求是:
- 您有某种GUI构建器框架,由于某种不幸的原因,它只获得类名或
Class<?>
实例或其他什么,并使用无参数构造函数。对于这个框架,您需要InteractiveChart<SimpleChart>
,图表为new SimpleChart()
- 您还有其他代码可以从更大的灵活性中获益:除了
SimpleChart
之外,您还想为不同的图表类型创建InteractiveChart<C>
实例
我有这个权利吗?
那么,问题是Java不允许您定义仅用于InteractiveChart<SimpleChart>
的构造函数—InteractiveChart
的任何构造函数都必须应用于任意C
—因此,您试图为任意InteractiveChart<C>
定义一个构造函数,该构造函数只是假设C
是SimpleChart
,这(我希望您同意(不是一个很好的假设,也不是Java支持的假设。
一个简单的解决方法是为GUI构建器框架创建InteractiveChart<SimpleChart>
的子类以进行实例化:
public class SimpleInteractiveChart extends InteractiveChart<SimpleChart> {
public SimpleInteractiveChart() {
super(new SimpleChart());
}
}
然后,您的GUI构建器框架可以使用new SimpleInteractiveChart()
,而您的其他代码仍然可以将InteractiveChart<C>
与它想要的任何类型参数一起使用。