我的目标很简单。我有一个SetInterface<T>

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


然后,我还有一个ExerciseInterface <T extends SetInterface<?>>

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


到目前为止,一切都很好。

问题出在哪里,我试图创建一个包含不同类的泛型集合,这些类都实现了ExerciseInterface。

我对Generics相当陌生,我一直认为自己已经解决了一些问题,但是我所做的只是推销错误。

我的addA()addB()方法几乎概括了这个问题

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);
    }
}


第一个add()给出以下错误。

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<?>>)


第二个add()给出此错误。

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<?>>)


重申一下,我有一个SetInterface,可以使用不同类型的Set来实现。

我也有不同的练习,可以反映出场景。例如:

BodyWeightExercise implements <SetInterface<BodyweightSet>>




WeightsExercise implements <SetInterface<WeightsSet>>


因此,我的目标是拥有一个不同练习的集合,以及一种使我能够添加到该集合中的方法。

任何帮助将由衷的感谢,我已经为此付出了一些努力。

干杯。

编辑:如果这不可行,那么朝着更可行方向的任何指针将不胜感激。

EDIT2:我将列表更改为List <ExerciseInterface<SetInterface<?>>> exerciseList;,并且add()方法上的错误都消失了。

但是,使用此测试代码。

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

weightsExercise.addSet(weightsSet);

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


我正在尝试将WeightsExercise添加到“锻炼”列表中,但是现在出现此错误。

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<?>>>


编辑3:扩展了ExerciseInterface和SetInterface的示例类(具体方法也在上面的接口中更新)

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;}
}


解决:该解决方案似乎正确地包含了所有内容。

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);}


}

最佳答案

您应该将列表声明更改为:

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


当您使用上限通配符时(我认为这里并不需要),除了null和以前从中获取的元素之外,您无法向列表添加任何内容。

这是因为,您不确切知道它实际引用的列表的具体参数化类型。例如:List<? extends CharSequence>可以引用ArrayList<String>ArrayList<StringBuffer>。因此,您不能在该列表中添加任何内容,因为它的输入类型不安全。您可能正在向StringBuffer添加ArrayList<String>对象,该对象在运行时将失败。



更新后:

我猜您正在尝试创建parallel class hierarchy,并且陷入困境。也许,您希望您的ExerciseInterface看起来像:

// 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);
}


然后您的类WeightsExercise将被修改为:

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

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


您还需要使您的Workout类通用,如下所示:

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

}


然后在ExerciseInterface<SetInterface<?>>类中将所有出现的ExerciseInterface<U>替换为Workout

并创建Workout的实例,如下所示:

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


这将使IMO设计正确。


推荐职位:


What is a difference between <? super E> and <? extends E>?
What is PECS (Producer Extends Consumer Super)?


参考文献:


Java Generics FAQ - Angelika Langer


What is bounded Wildcards?
Do generics help designing parallel class hierarchies?

09-03 20:06