6

It's hard to explain in words, but Java Generics are given me an unexpected result. I expected that if I say a list is of type ? extends Object, I could store anything in there. Therefore, if the list of of type Wrapper<? extends Object>, I could store any kind of Wrapper in there. And so on. That makes sense to me. But let's assume we have:

private static class Wrapper<T> {
    public Wrapper(T t) { /**/ }
}

And I want something like:

private static final List<Wrapper<Wrapper<? extends Object>>> ls1 = new ArrayList<>();

Note that this gives me an error:

public static <T> doit(T t) {
    Wrapper<Wrapper<T>> l1 = new Wrapper<>(new Wrapper<>(t));
    ls1.add(l1); // nok
    // add (Wrapper<Wrapper<? extends java.lang.Object>>) in List
    // cannot be applied to (Wrapper<Wrapper<T>>
}

But if I wrap the Wrapper in a secondary Wrapper (rs), then:

private static class C<T> extends Wrapper<Wrapper<T>> {
    public C(T t) {
        super(new Wrapper<>(t));
    }
}

private static final List<C<? extends Object>> ls2 = new ArrayList<>();

public static <T> doit(T t) {
    ls2.add(new C<>(t)); // ok
}

Note that it is the same thing; this make no sense to me.

PS. In my case, I'm not making a wrapper wrapper, but a ThreadLocal of a generic class.

Luan Nico
  • 4,792
  • 2
  • 27
  • 50

3 Answers3

2

I think

? extends Object ---> equals ?

You can do like this

List<Wrapper<Wrapper<?>>> ls1 = new ArrayList<>();

And

Wrapper<Wrapper<?>> l1 = new Wrapper<>(new Wrapper<>(t));
ls1.add(l1); // OK
Loc
  • 8,364
  • 6
  • 36
  • 73
0

To be honest, I would have to look into it in more detail why it does not work. Its most likely related to the generic type erasure. What I have is a workaround, I know this solution looses the typ for the l1 object. If this is ok for you take it.

Wrapper<Wrapper<? extends Object>> l1 = new Wrapper<>(new Wrapper<>(t));
ls1.add(l1);
mh-dev
  • 4,668
  • 2
  • 23
  • 23
0

Basically you need to decide what's more important, being generic over what you want to put into the wrapper, over or what you take out of the wrapper (you cannot safely both). After deciding, you will then need to choose between extends and super.

If you just want to store the pointer then List<Wrapper<Wrapper>> will suffice, but then you will need to do some casting later.


When you are using extends then? extends Foo means that any data type returned by the structure will a subtype of type Foo (or Foo itself).

For example if you have void doIt1( List< ? extends Mammal> l ){} you could pass in List<Human>, List<Primate>, List<Simian> or a List<Mammal>.

However when using ? extends you don't know what is safe to put in.

Consider this:

void addElephant( List< ? extends Mammal> l  ){
    l.add( new Elephant() ); //wrong!
}

This is clearly unsafe! I could have passed in a List<Human> as l and now my list of Humans has a Elephant in it!

On the other hand you have super where ? super Foo means that the container can take a Foo.

So for void doIt2( List< ? extends Human> l ){} as you could pass in List<Human>, List<Primate>, List<Simian>, List<Mammal>, or a List<Object>.

void ( List< ? super Human> l  ){
    l.add( new Human() );
}

And this is fine, but you cannot guarantee anything about what you will get out of the container.

void ( List< ? super Human> l  ){
    Human h = l.get(0); //wrong!
}

That is because if l was really a List<Primate> it could have a mixture of Humans and Gorillas and there is no guarantee that the first element will be a Human.

For this you will get the acronym P.E.C.S = "[a] Producer ** Extends, [but a] Consumer Supers".

If you're container is returning object to a method (eg get), then you are a "producing" (so use extends), otherwise if you are accepting elements (eg add) to a container, the container is "consuming" the object so use "super". (Nb: tricky parts to remember about PECS is that it is from the point-of-view of the container and not the calling code!).

If you want to both produce and consume you'll need to have a concrete type list List<Primate> where you can add Humans and retrieve Primates.


See also:

Community
  • 1
  • 1
Sled
  • 16,514
  • 22
  • 110
  • 148
  • To be honest it seems that you pot a high amount of effort into this, but he simply wants to keep the type safety of l1 and your writing an essay about the method. – mh-dev Oct 28 '16 at 14:33
  • @mh-dev yeah, I wanted to explain what was wrong with a naive approach to his question _and_ as a reference to be able to point to for our interns and junior developers. – Sled Oct 28 '16 at 14:41