4

I've got an abstract generic collections class GCollection, and a class that extends that called GStack.

To test the implementation I have an abstract JUnit test class, which I extend for each of the GCollection implementations I do:

public abstract class GCollectionTest<T extends GCollection<E>, E> {
    private GCollection<? extends Object> collection;
    protected abstract GCollection<T> createInstance();

    @Before
    public void setup() throws Exception {
        collection = createInstance();
    }

    // Tests down here. 

This is extended like so:

public class GStackCollectionInterfaceTest<S extends GCollection<E>> {
    protected GDSStack<? extends Object> createInstance() {
        return new GDSStack<String>();
    }
}

I test first with a GStack holding String objects, then re-run the tests with Date objects to ensure it works with different object types.

@Test
public void testIsEmpty() {
    assertTrue(collection.isEmpty()); // Fresh Stack should hold no objects
    collection.add(new String("Foo")); // Error here.
    assertFalse(collection.isEmpty());
}

The error given is:

The method add(capture#24-of ? extends Object) in the type GCollection is not applicable for the arguments (String)

My understanding of the error is that I can't put a String object into a GCollection<T extends GCollection<E>> object, but I don't think that's what I'm trying to do.

What am I doing wrong?

How can I solve this error while maintaining tests that are as generic as possible?

simont
  • 68,704
  • 18
  • 117
  • 136

1 Answers1

7

The type of collection is GCollection<? extends Object>. It is not possible to add anything to this collection, see: can't add value to the java collection with wildcard generic type.

There's no need for wildcards or bounds in the subclass so you can simplify the generics. Something like:

abstract class GCollectionTest<T> {
    protected Collection<T> collection;

    protected abstract Collection<T> createCollection();
    protected abstract T createObject();

    @Before
    public void setup() throws Exception {
        collection = createCollection();
    }

    @Test
    public void testIsEmpty() {
        assertTrue(collection.isEmpty());
        collection.add(createObject());
        assertFalse(collection.isEmpty());
    }
}

class GStackCollectionInterfaceTest extends GCollectionTest<String> {
    protected GDSStack<String> createCollection() {
        return new GDSStack<String>();
    }

    protected String createObject() {
        return new String("123");
    }
}

Using different types with the collection is allowed because of the generic type, and checked by the compiler, so it doesn't really need testing. I would just test the different container types, but you could create another subclass that uses Date instead of String.

Community
  • 1
  • 1
fgb
  • 18,439
  • 2
  • 38
  • 52