1

There is an external interface named Shape (name changed, but you get the idea).

I have a class that has a constructor that takes

Collection<? extends Shape>

This class is under test. I am writing the unit tests. I want to create a Collection that extends Shape to provide to the constructor.

I notice that no class in our project implements Shape, so I write an inner class:

private class TestShape implements Shape {
    private static final long serialVersionUID = 1L;

    @Override
    public String getAuthority() {
        return "Foo";
    }
}

Then I try to create my Collection ...

private ArrayList<? extends Shape> shapes;
shapes = new ArrayList<Shape>();
shapes.add(new TestShape());

Eclipse however is unhappy with me ...

The method add(capture#1-of ? extends Shape) in the type ArrayList<capture#1-of ? extends Shape> is not applicable for the arguments (Test.TestShape)

Why?

edit: Lots of editing because generics syntax makes SO unhappy

Lurk21
  • 2,198
  • 11
  • 34
  • 52
  • This is PECS at work! – awksp May 27 '14 at 20:44
  • 1
    @user3580294 I'm not familiar with that abbreviation. – nanofarad May 27 '14 at 20:44
  • 1
    @hexafraction Producer Extends Consumer Super? For wildcards in generics -- if the list is producing things, you use `extends`, and if you're sticking things into the list, you use `super`. – awksp May 27 '14 at 20:46
  • @user3580294 May I add that to my post with attribution? – nanofarad May 27 '14 at 20:46
  • @hexafraction You could add that, but don't attribute it to me -- I first heard of it from Joshua Bloch in Effective Java. Don't know if that's where it originated from, but that's where I first learned it. – awksp May 27 '14 at 20:47
  • @user3580294 Thanks, I'll have to read that sometime. – nanofarad May 27 '14 at 20:48

3 Answers3

4

Let's imagine that we have TestShape implements Shape, and EvilShape also implements Shape. Now imagine the following:

private ArrayList<? extends Shape> shapes; //OK
shapes = new ArrayList<EvilShape>(); // OK
authorities.add(new TestShape()); // Type safety issue

Clearly, such a thing won't work. Instead, do the following:

private ArrayList<Shape> shapes; //OK
shapes = new ArrayList<Shape>(); // OK
authorities.add(new TestShape()); // OK

Or the following:

private ArrayList<? super Shape> shapes; //OK
shapes = new ArrayList<Shape>(); // Or, for that matter, an `ArrayList<Object>`.
authorities.add(new TestShape()); // OK

This will work, as you can add a subclass/implementation of Shape to a List<Shape>. Using an exact type parameter and not a wildcard will prevent the following:

private ArrayList<Shape> shapes; //OK
shapes = new ArrayList<EvilShape>(); // DANGER! Java does not allow this

The abbreviation PECS helps remember this: Producer Extends, Consumer Super. This comes from Joshua Bloch's Effective Java.

nanofarad
  • 36,174
  • 4
  • 79
  • 104
  • This certainly seems to be the right answer, but I'm doing exactly this and still getting the eclipse compilation error. – Lurk21 May 27 '14 at 20:51
  • Ah, I thought I had found the answer was with my Shape import, but no, that import is correct. Still confused. – Lurk21 May 27 '14 at 20:52
  • @Lurk21 What if you avoid the wildcard and just go with a basic parameter of `Shape`? – nanofarad May 27 '14 at 20:54
0

I have a class that has a constructor that takes

Collection<? extends Shape>

...

Then I try to create my Collection ...

private ArrayList<? extends Shape> shapes;

I think one thing that may be confusing is that just because there is a method parameter like that (Collection<? extends Shape>), you don't need to create a variable like that. Think of the purpose of a variable/parameter that uses wildcard generics (? extends X or ? super Y) as a way to write some "generic" code that will work with any value from a family of similar types rather than the usual "a value from a specific type".

--

The perfect example of this is the signature for Collection.copy():

public static <T> void copy(List<? super T> dest,List<? extends T> src)

This method allows me to pass an instance of List<Integer> and have its values copied into an instance of List<Number>. It also allows me to copy List<Integer> into List<Integer>. It lets me use the same code with different types ("family of types"). I can test it with using wildcard generic variables at all:

List<Integer> src = new ArrayList<>();
src.add(1);
List<Number> dest = new ArrayList<>();

Collection.copy(src, dest);

assert(src.get(0), dest.get(0));

--

The big tradeoff with using a variable or parameter like List<? super T> or List<? extends T> is that because its not a variable for a specific type, there are limitations on what you can assume about the instance it points to and therefore you are limited in what you can do with it.

PECS is a tiny bit of an oversimplification, but it makes a great mnemonic: PECS = "producer extends, consumer super" means that a variable (or parameter) for a generic wildcard type that uses extends can only be used to "produce" values, i.e. you can safely only read from it, you can't add to it. Similarly, a variable for a generic wildcard type that uses super can only be used to "consume" values - you can safely only feed it values (add), but there are limitations on what you can assume about the values that could be "read" from it.

So, for your example, I would avoid using a variable with a wildcard generic at all, just pass in different List<> types to the constructor. If you must use a variable with a wildcard generic, that use a variable to the specific type to fill it because the variable with the wildcard generic that uses extends can't be used to fill it:

private ArrayList<Shape> shapesSpecific;
private ArrayList<? extends Shape> shapesExtends;
shapesExtends = shapesSpecific = new ArrayList<Shape>();

shapesSpecific.add(new TestShape());   // fine
//shapesExtends.add(new TestShape());  // error - but the line above added a value already

// Another option - create it already filled:
// shapeExtends = Arrays.asList(new TestShape());

new Foo(shapesSpecific);  // fine
new Foo(shapesExtends);   // fine

--

You might find this question/answer useful:

How can I add to List<? extends Number> data structures? - here's an example from it:

The wildcard declaration of List<? extends Number> foo3 means that the variable foo3 can hold any value from a family of types (rather than any value of a specific type). It means that any of these are legal assignments:

List<? extends Number> foo3 = new ArrayList<Number>;  // Number "extends" Number
List<? extends Number> foo3 = new ArrayList<Integer>; // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>;  // Double extends Number

So, given this, what type of object could you add to List foo3 that would be legal after any of the above possible ArrayList assignments:

  • You can't add an Integer because foo3 could be pointing at a List<Double>.
  • You can't add a Double because foo3 could be pointing at a List<Integer>.
  • You can't add a Number because foo3 could be pointing at a List<Integer>.

You can't add any object to List<? extends T> because you can't guarantee what kind of List it is really pointing to, so you can't guarantee that the object is allowed in that List. The only "guarantee" is that you can only read from it and you'll get a T or subclass of T.

This is a similar question/answer:

Difference between <? super T> and <? extends T> in Java

Community
  • 1
  • 1
Bert F
  • 78,021
  • 11
  • 97
  • 121
0

If this is for a unit test, just make a collection of Shapes:

import java.util.ArrayList;
import java.util.Collection;

public class test {
    public interface Shape {
        String getAuthority();

    }

    private static class TestShape implements Shape {

        @Override
        public String getAuthority() {
            return "Foo";
        }
    }

    public static void method(Collection<? extends Shape> shapes){

    }

    public static void main(String ... args){
        ArrayList<Shape> shapes;
        shapes = new ArrayList<Shape>();
        shapes.add(new TestShape());
        method(shapes);
    }
}

this compiles cleanly and is likely how the method is used in actual practice.

Andreas
  • 4,410
  • 2
  • 19
  • 28