特定枚举类型的“集”,但具有泛型

2022-09-01 07:50:34

假设我有一个抽象类

public abstract class Trainer<T extends Animal>{}

我有特定的培训师,如:

public DogTrainer extends Trainer<Dog>{}
public HorseTrainer extends Trainer<Horse>{}

这些“训练师”中的每一个都有一套固定的技巧,他们可以训练动物去做,我想用Enums来做。所以我有一个接口:

public interface TrainingActions<T extends Animal>{}

在每个培训师中,我都有一个实现此接口的枚举。所以:

public DogTrainer extends Trainer<Dog>{
  public enum Trainables implements TrainingActions<Dog>{
    BARK, BITE, ROLLOVER, FETCH;
  }
}

public HorseTrainer extends Trainer<Horse>{
  public enum Trainables implements TrainingActions<Horse>{
    JUMP, TROT, REARUP;
  }
}

现在,在每个训练器类中,我想要一个方法,说“trainingComplete”,将其中一个枚举作为输入并将其保存到一个集合中。所以

public DogTrainer extends Trainer<Dog>{
  public enum Trainables implements TrainingActions<Dog>{
    BARK, BITE, ROLLOVER, FETCH;
  }
  public Set<Trainables> completed = new HashSet<Trainables>();
  public void trainingComplete(Trainables t){completed.add(t);}
}

但是,与其在每个特定训练器中定义“已完成”集,在每个训练器中定义“训练完成”方法,不如在父“训练器”类中定义一些可以强制执行枚举类型的内容...所以这是枚举和泛型的奇怪组合。

这可能吗?


答案 1

要指定接口的边界并且它是枚举,您需要一个通用交集,如下所示:

class MyClass<T extends Enum<T> & SomeInterface> {}

请注意,当类和接口相交时,该类必须出现在接口之前。

在这种情况下,你想要的泛型功夫要复杂一个级别,因为TrainingActions枚举的界面本身必须引用回动物类型。

class Trainer<A extends Animal, T extends Enum<T> & TrainingActions<A>> {}

基于您发布的代码编译的完整工作示例是:

public class Animal {}

public interface TrainingActions<T extends Animal> {}

/**
 * A trainer that can teach an animal a suitable set of tricks
 * @param <A> The type of Animal
 * @param <T> The enum of TrainingActions that can be taught to the specified Animal
 */
public abstract class Trainer<A extends Animal, T extends Enum<T> & TrainingActions<A>> {
    private Set<T> completed = new HashSet<T>();
    public void trainingComplete(T t) {
        completed.add(t);
    }
}

public class Dog extends Animal {};

public class DogTrainer extends Trainer<Dog, DogTrainer.Trainables> {
    public enum Trainables implements TrainingActions<Dog> {
        BARK, BITE, ROLLOVER, FETCH;
    }
}

但是,我会更进一步,在它们所适用的特定类中定义几个枚举:TrainingActionsAnimal

public class Dog extends Animal {
    public enum BasicTrainables implements TrainingActions<Dog> {
        SIT, COME, STAY, HEEL;
    }
    public enum IntermediateTrainables implements TrainingActions<Dog> {
        BARK, BITE, ROLLOVER, FETCH;
    }
    public enum AdvacedTrainables implements TrainingActions<Dog> {
        SNIFF_DRUGS, FIND_PERSON, ATTACK, GUARD;
    }
};

public class PuppyTrainer extends Trainer<Dog, Dog.BasicTrainables> {}

public class ObedienceTrainer extends Trainer<Dog, Dog.IntermediateTrainables> {}

public class PoliceTrainer extends Trainer<Dog, Dog.AdvacedTrainables> {}

答案 2

尽管进行了设计分析,但我认为你的情况(最接近你已经创建的设计)的答案是:

  • 您的是DogTrainer.TrainablesTrainingActions<Dog>
  • 您的是HorseTrainer.TrainablesTrainingActions<Horse>

通常,您的可训练对象已经是训练行动<T扩展动物>

所以你可以只提供 ,因为你已经在T中拥有你的动物信息Set<TrainingActions<T>> completed

abstract class Trainer<T extends Animal> {
    Set<TrainingActions<T>> completed = new HashSet<TrainingActions<T>>();

    public void add(TrainingActions<T> t ) {
        completed.add( t );
    }
}

你拥有你想要的。

用法:

DogTrainer tr = new DogTrainer();
tr.add( DogTrainer.Trainables.BITE );

如果您确定要为接口强制实施枚举:

public <E extends Enum<?> & TrainingActions<T>> void add( E t ) {
   completed.add( t );
}