我只是尝试创建一个有界类型的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>
,但我想知道为什么这是必要的,如果有任何方法可以实现工作?
答案 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
是可写的,现在是什么?
如果fooList
是List<? 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}}这样的静态工厂方法来修改构造函数不做推理的问题。这种做法现在已经过时了。