假设我有一个包含WidgetHolder
个实例的类Widget
。在内部,我用List<Widget>
备份它。我保证它是不可变的,所以在构造之后没有小部件被添加到持有者。
WidgetHolder
还公开了一个getWidgets()
操作,该操作返回小部件列表的不可变视图。
我希望有一个包含SpecificWidgetHolder
个实例的派生SpecificWidget
类,其getWidgets()
的返回类型为Collection<SpecificWidget>
。
我有相同的保证,即SpecificWidgetHolder
构造函数采用SpecificWidget
个实例的集合,不会以任何方式对其进行修改。
鉴于WidgetHolder
和SpecificWidgetHolder
都是不可变的,通常的反对证明通用不变性似乎不适用(?)
在Java中有没有一种清晰的表达方式?
谢谢!
答案 0 :(得分:1)
试试这个:
public class WidgetHolder<T extends Widget>
{
public List<T> getWidgets()
{
return (widgets);
}
private List<T> widgets;
} // class WidgetHolder
public class SpecificWidgetHolder extends WidgetHolder<SpecificWidget>
{
}
答案 1 :(得分:1)
您描述的场景非常常见:您在内部存储了一组对象,并希望公开此集合的只读版本。这通常使用Collections#unmodifiableCollection
方法(以及其他集合类型的特定方法)来完成:
class WidgetHolder {
private final List<Widget> widgets = new ArrayList<Widget>();
public List<Widget> getWidgets() {
return Collections.unmodifiableList(widgets);
}
}
但请注意如果该集合无论如何都是不可修改的,您可以通过利用集合不可修改的事实来实现所需的协方差。如果它是不可修改的,您可以将返回类型声明为
public List<? extends Widget> getWidgets() { ... }
这样,每个人都可以像以前一样使用列表。他可以阅读并迭代它。他不能添加新元素,因为它是不可修改的 - 并且一个很好的副作用是这种不可修改性(在某种程度上)在这里可见语法:
List<Widget> widgets = widgetHolder.getWidgets();
widgets.add(new Widget()); // throws UnsupportedOperationException AT RUNTIME!
VS
List<? extends Widget> widgets = widgetHolder.getWidgets();
widgets.add(new Widget()); // Does not compile!
(请注意,widgets.remove(x)
或widgets.add(null)
之类的调用仍会编译,但会抛出UnsupportedOperationException
- 它只是一个微妙的提示)
关于您的问题,关键点是您可以使用更具体的返回类型覆盖此方法。 这是真正的协方差。
因此,在课程SpecificWidgetHolder
中,您可以选择3种覆盖此方法的方法:
使用与超类中相同的类型:
public List<? extends Widget> getWidgets()
使用特定类型,您知道它可以分配给超类的类型:
public List<SpecificWidget> getWidgets()
再次使用通配符类型,为进一步的专业化留出空间(如VerySpecificWidgetHolder
):
public List<? extends SpecificWidget> getWidgets()
如何使用课程有细微差别。特别是,关于&#34;特异性&#34;的信息。可供来电者使用。我试图在这个例子中指出这一点:
// With option 1, you only receive Widgets - even
// from a SpecificWidgetHolder
List<? extends Widget> widgets = specificWidgetHolder.getWidgets();
Widget widget = widgets.get(0);
// With option 3., you still know that a SpecificWidgetHolder
// provides a list of SpecificWidgets
List<? extends SpecificWidget> specificWidgets = specificWidgetHolder.getWidgets();
SpecificWidget specificWidget = specificWidgets.get(0);
基本上,您必须确定拥有SpecificWidgetHolder
的人是否应该能够从中获取SpecificWidget
个实例。
旁注:如另一个答案所示,这也可以用泛型来解决。但我不认为这是必要的。我通常对将类型参数附加到类有点犹豫,因为它们可能比非泛型类型更难理解和维护。泛型(类型参数)就像盐:在正确的位置和正确的剂量,他们可以使事情简单更好。但是当你被它们带走,并最终得到嵌套的泛型和方法签名如<S, T extends Number> Foo<S, ? super T> getFoo()
时,这可能会妨碍可读性和可维护性