1

My goal is pretty simple. I've a SetInterface<T>:

public interface SetInterface<T>
{   
    public T duplicateSet();
}

I then also have an ExerciseInterface <T extends SetInterface<?>>

public interface ExerciseInterface <T extends SetInterface<?>>
{   
    public T getSet(int pos);
    public void addSet(T set);
}

Everything works great up to this point.

The problem comes where I try to create a Generic Collection containing different classes which all implement the ExerciseInterface.

I am fairly new to Generics and I keep thinking I have solved something but all I am doing is pushing the error about.

The problem is pretty much summed by my addA() and addB() methods

public class Workout {  

    private String name;
    private List <? extends ExerciseInterface<SetInterface<?>>> exerciseList;

// Constructor
public Workout(String name) 
{
    this.name = name;
    exerciseList = new ArrayList<ExerciseInterface<SetInterface<?>>>();
}   

public String getName() {
        return name;
    }

public void setName(String name) {
        this.name = name;
    }

public void addExerciseA(ExerciseInterface<SetInterface<?>> exercise {
        exerciseList.add(exercise);
    }

public <T extends ExerciseInterface<SetInterface<?>>> void addExerciseB(T  exercise {
        exerciseList.add(exercise);
    }

public ExerciseInterface<SetInterface<?>> getExercise(int pos) {
        return exerciseList.get(pos);
    }
}

The first add() gives the following error..

The method add(capture#2-of ? extends ExerciseInterface<SetInterface<?>>) in the type List<capture#2-of ? extends ExerciseInterface<SetInterface<?>>> is not applicable for the arguments (ExerciseInterface<SetInterface<?>>)

The second add() gives this error..

The method add(capture#2-of ? extends ExerciseInterface<SetInterface<?>>) in the type List<capture#2-of ? extends ExerciseInterface<SetInterface<?>>> is not applicable for the arguments (ExerciseInterface<SetInterface<?>>)

Just to reiterate, I have a SetInterface that different types of Sets implement.

I also have different exercises which mirror the Sets. For example:

BodyWeightExercise implements <SetInterface<BodyweightSet>>

and

WeightsExercise implements <SetInterface<WeightsSet>>

So my goal is to have a Collection of different exercises and a method that allows me to add to that collection.

Any help would be sincerely appreciated, I have lost a few hairs to this already.

Cheers.

EDIT: If this is not doable any pointers in a more feasible direction would be just appreciated.

EDIT2: I changed the List to List <ExerciseInterface<SetInterface<?>>> exerciseList; and the error on the add() methods both dissappear.

However, using this test code..

WeightsSet weightsSet = new WeightsSet();       
WeightsExercise weightsExercise = new WeightsExercise();

weightsExercise.addSet(weightsSet);

Workout workout = new Workout("Wok");
workout.addExerciseA(weightsExercise);     <-- ERROR

I am trying to add a WeightsExercise to Workout's List, but I get this error now..

Bound mismatch: The generic method addExerciseB(T) of type Workout is not applicable for the arguments (WeightsExercise). The inferred type WeightsExercise is not a valid substitute for the bounded parameter <T extends ExerciseInterface<SetInterface<?>>>

EDIT 3: Example classes extending ExerciseInterface and SetInterface (Concrete methods update in the interfaces above as well)

public class WeightsExercise implements ExerciseInterface<SetInterface<WeightsSet>>
{
@Override
public SetInterface<WeightsSet> getSet(int pos)    {return null;}

@Override
public void addSet(SetInterface<WeightsSet> set)    {}  
}

public class WeightsSet implements SetInterface<WeightsSet>
{
@Override
public WeightsSet duplicateSet()    {return null;}  
}

SOLVED: This solution seems to encompass everything correctly.

public class Workout
{   
private String name;
private List <ExerciseInterface<? extends SetInterface<?>>> exerciseList;

// Constructor
public Workout(String name) 
{
    this.name = name;
    exerciseList = new ArrayList<ExerciseInterface<? extends SetInterface<?>>>();
}   

public String getName()                                                                         {return name;}
public void setName(String name)                                                                {this.name = name;} 
public void addExerciseA(ExerciseInterface<? extends SetInterface<?>> exercise)                 {exerciseList.add(exercise);}
public ExerciseInterface<? extends SetInterface<?>> getExercise(int pos)                        {return exerciseList.get(pos);}

}

7
  • You cannot add to something you don't know the type of. Commented Aug 28, 2013 at 18:06
  • So would you have a suggestion on how to have a collection that contains a bunch of classes extending ExerciseInterface<SetInterface<?> ?
    – mgibson
    Commented Aug 28, 2013 at 18:09
  • You need to show us how your classes look like. WeigthsSet, WeightsExercise. It's not clear.
    – Rohit Jain
    Commented Aug 28, 2013 at 18:21
  • What is the use of ExerciseInterface?
    – Rohit Jain
    Commented Aug 28, 2013 at 18:27
  • Edit 3 shows some concrete examples
    – mgibson
    Commented Aug 28, 2013 at 18:32

1 Answer 1

2

You should change your list declaration to:

List<ExerciseInterface<SetInterface<?>>> exerciseList;

When you use upper bounded wildcards, which I don't think you really need here, you can't add anything to the list, apart from null, and elements previously fetched from it.

That's because, you don't know exactly what concrete parameterized type of list it actually references. For e.g.: List<? extends CharSequence> can reference a ArrayList<String> or ArrayList<StringBuffer>. So, you can't add anything to that list, because it's not type safe. You might possibly be adding a StringBuffer object to ArrayList<String>, that will fail at runtime.


After update:

I guess you are trying to create a parallel class hierarchy, and got messed up in that. Perhaps, you wanted your ExerciseInterface to look like:

// Notice the use of `T` in `SetInterface<T>` instead of `SetInterface<?>`
interface ExerciseInterface<T extends SetInterface<T>> {
    SetInterface<T> getSet(int pos);
    void addSet(SetInterface<T> set);
}

and then your class WeightsExercise would be modified as such:

class WeightsExercise implements ExerciseInterface<WeightsSet> {
    @Override
    public SetInterface<WeightsSet> getSet(int pos)    {return null;}

    @Override
    public void addSet(SetInterface<WeightsSet> set)    {}  
}

You would also need to make your Workout class generic like this:

public class Workout<U extends SetInterface<U>, T extends ExerciseInterface<U>> {

}

Then replace all occurrence of ExerciseInterface<SetInterface<?>> with ExerciseInterface<U> in your Workout class.

And create the instance of Workout like this:

Workout<WeightsSet, WeightsExercise> workout = new Workout<>("Wok");
workout.addExerciseA(weightsExercise);

This will what make a correct design IMO.


Recommended post:

References:

4
  • Cheers Rohit, I'll take that on board. I also have been reading up on some of those resources, I shall look further though. I took the upper bounded wildcard out though and now there is a different generic related problem. Please see Edit 2
    – mgibson
    Commented Aug 28, 2013 at 18:18
  • Will check that out ASAP. Appreciate your time
    – mgibson
    Commented Aug 28, 2013 at 20:03
  • Hmm Rohit, there seems to be some great stuff in there. However, what I am trying to achieve is be able to add WeightsSet and WeightsExercise to the Workout yes but also another Exercise that implements ExerciseInterace.
    – mgibson
    Commented Aug 28, 2013 at 22:32
  • I believe I have it solved, following some of your advice and tweaking the Workout class to contain different exercises. The goal is to use polymorphic methods anyway so the current solution (see answer) I believe incorporates that. Thanks for your help!
    – mgibson
    Commented Aug 28, 2013 at 22:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.