Why does ListDataModel not work with a bounded type parameter?
I just tried to create a ListDataModel
with a bounded type, li开发者_C百科ke this:
DataModel<? extends Foo> model = new ListDataModel<? extends Foo>(fooList);
, where fooList
is of the type List<? extends Foo>
. I get the following error:
unexpected type
required: class or interface without bounds
found: ? extends Foo
My current workaround is to copy my data into an ArrayList<Foo>
, and build a DataModel<Foo>
from that, but I would like to know why this is necessary, and if there is any way to make it work?
<? extends Foo>
means "some type, I don't know which one, which is or extends Foo". When constructing the data model, you need to tell him which type of data it contains, not just one unknown type.
Just construct your data model like this:
DataModel<? extends Foo> model = new ListDataModel<Foo>(fooList);
Unfortunately, the ListDataModel<Foo>
constructor only accepts a List<Foo>
, and not a List<? extends Foo>
. It seems to me like a misconception. For example the HashSet<E>
constructor takes a Collection<? extends E>
as argument. If you accept the type safety warning, just casting your list to a List<Foo>
should work.
Since ListDataModel
is read only, it is wrong for its constructor to accept only List<E>
. It's ok to cast to bypass this design flaw.
A more general question: suppose ListDataModel
is writable, what now?
If fooList
is a List<? extends Foo>
, then it certainly is a List<W>
for a concrete W
which extends Foo
. Then we should be able to new ListDataModel<W>(fooList)
, the result type is a DataModel<W>
, which is assignable to DataModel<? extends Foo> model
.
This is how compiler internally reason about wildcards (wildcard capture); too bad we can't access W
directly in Java (it's a so called non-denotable type), but we can cause wildcard capture through method invocation:
static <W> ListDataModel<W> make(List<W> list)
{
return new ListDataModel<W>(list);
}
List<? extends Foo> fooList = ...;
DataModel<? extends Foo> model = make( fooList );
When compiling make( fooList )
, compiler internally refines the type of fooList
to a List<W>
where <W extends Foo>
; then the rest works natually.
In Java 7, type inference is extended to constructors with <>
syntax
List<? extends Foo> fooList = ...;
DataModel<? extends Foo> model = new ListDataModel<>(fooList); // OK in Java 7
With <>
, constructor call is pretty much the same as method calls; so make()
is no longer needed. Prior to Java 7, static factory methods like make()
were needed to amend the problem that constructors don't do inference. That practice is now obsolete.
精彩评论