简体   繁体   中英

Why does ListDataModel not work with a bounded type parameter?

I just tried to create a ListDataModel with a bounded type, like this:

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

, where fooList is of the type List<? extends Foo> 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> <? 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> List<? extends Foo> . It seems to me like a misconception. For example the HashSet<E> constructor takes a Collection<? extends E> 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> 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 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM