为什么ListDataModel不能使用有界类型参数?

时间:2011-08-29 19:16:17

标签: java generics jsf-2

我只是尝试创建一个有界类型的ListDataModel,如下所示:

DataModel<? extends Foo> model = new ListDataModel<? extends Foo>(fooList);

,其中fooList属于List<? extends Foo>类型。我收到以下错误:

unexpected type
  required: class or interface without bounds
  found: ? extends Foo

我目前的解决方法是将我的数据复制到ArrayList<Foo>,并从中构建DataModel<Foo>,但我想知道为什么这是必要的,如果有任何方法可以实现工作?

2 个答案:

答案 0 :(得分:5)

<? extends Foo>表示“某种类型,我不知道哪一种,哪种是Foo”或“Foo”。在构建数据模型时,您需要告诉他包含哪种类型的数据,而不仅仅是一种未知类型。

只需构建您的数据模型:

DataModel<? extends Foo> model = new ListDataModel<Foo>(fooList);

不幸的是,ListDataModel<Foo>构造函数只接受List<Foo>,而不是List<? extends Foo>。在我看来,这似乎是一种误解。例如,HashSet<E>构造函数将Collection<? extends E>作为参数。如果您接受类型安全警告,只需将列表转换为List<Foo>即可。

答案 1 :(得分:1)

由于ListDataModel是只读的,因此构造函数只接受List<E>是错误的。可以绕过这个设计缺陷。

一个更一般的问题:假设ListDataModel是可写的,现在是什么?

如果fooListList<? extends Foo>,则对于List<W>扩展W的具体Foo肯定是new ListDataModel<W>(fooList)。然后我们应该能够DataModel<W>,结果类型是DataModel<? extends Foo> model,可以分配给W

这是编译器内部推理通配符的方式(通配符捕获);太糟糕了,我们无法直接在Java中访问static <W> ListDataModel<W> make(List<W> list) { return new ListDataModel<W>(list); } List<? extends Foo> fooList = ...; DataModel<? extends Foo> model = make( fooList ); (它是所谓的非可表示类型),但我们可以通过方法调用引起通配符捕获:

make( fooList )

编译fooList时,编译器会在内部将List<W>的类型细化为<W extends Foo> <>;然后其余的工作自然。

在Java 7中,类型推断扩展到具有List<? extends Foo> fooList = ...; DataModel<? extends Foo> model = new ListDataModel<>(fooList); // OK in Java 7 语法

的构造函数
<>

使用make(),构造函数调用与方法调用几乎相同;因此不再需要make()。在Java 7之前,需要像{{1}}这样的静态工厂方法来修改构造函数不做推理的问题。这种做法现在已经过时了。