tags:

views:

112

answers:

3

Here is an example of some code I'm working on:

public interface FooMaker<T extends Enum<T> & FooType>
{
      public List<Foo<T>> getFoos(String bar);
}

Let's further assume there will be many different concrete implementations of FooMaker. So I wrote some code to utilize the FooMakers.

FooMaker<?> maker = Foos.getRandomMaker();
List<Foo<?>> fooList = maker.getFoos("bar");  //error here!

The second line of code causes the issue, eclipse tells me the code should be:

FooMaker<?> maker = Foos.getRandomMaker();
List<?> fooList = maker.getFoos("bar");

I'm having trouble understanding why the Foo declaration as the parameterized type in List has to go away to make the return type correct.

Any ideas?

A: 

Because you are declaring maker as FooMaker<?>. Why wouldn't you declare it as the type of AwesomeFooMaker if you know what specific T AwesomeFooMaker returns?

matt b
I'll edit my code a smidge. I actually won't know which FooMaker I'll be getting.
Nick
It doesn't really matter. By declaring the type parameter as `?` you are stating "I have no idea what type this will return` and the compiler essentially acts as if any method related to the type parameter is now `?`, even if it's `List<?>`. Couldn't you use `FooMaker<? extends FooType> maker`?
matt b
The interface's return type is List<Foo<T>>, with my declaration I'm saying it could be any type T. Even if I declare FooMaker<? extends FooType> maker, I still get a similar error. I'm trying to understand why the Foo parameterized type is being ignored.
Nick
+1  A: 

Usual:

class Bar {}
class Baz {}

FooMaker<?> maker = new FooMaker<Bar>();
List<Foo<?>> fooList = maker.getFoos("bar");  //error here!
fooList.add(new Foo<Baz>());                  //cock-up here!
Tom Hawtin - tackline
I chose the other guy as answering the question because he included a way to work around my limitations, but I appreciate your input on why it doesn't work as I expected. +1
Nick
+1  A: 

Try this instead:

List<? extends Foo<? extends Enum<?>>> fooList = maker.getFoos("bar");

The problem is that if this has been allowed:

List<Foo<?>> fooList = maker.getFoos("bar");

Then by extension, you would've been able to get away with this as well:

Foo<?> foo1 = new Foo<String>();
Foo<?> foo2 = new Foo<Integer>();
fooList.add(foo1);
fooList.add(foo2);

Which would invalidate the generic contract of the returned list.

To prevent this, the java compiler forces the return type to be wildcard-based, meaning Foo can be used as a return-type (to pull elements out of the list) but you will not be able to add wildcard-based Foo types to your list.

Luke Hutteman
That makes perfect sense actually. The invalidation is where everything gets whack. Your suggestion works as advertised. Thanks for the help.
Nick