34 votes

"Sets" d'un type d'énum particulier mais avec des génériques

Disons que j'ai une classe abstraite

public abstract class Trainer<T extends Animal>{}

J'ai des formateurs comme :

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

Chacune de ces "formateurs" a un ensemble fixe de trucs qu'ils peuvent former l'animal pour le faire, que je voudrais utiliser les Énumérations pour. J'ai donc une interface:

public interface TrainingActions<T extends Animal>{}

et dans chacun des formateurs, j'ai un Enum qui implémente cette interface. Donc:

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

Maintenant, dans le chacun des le Formateur classes, j'aimerais une méthode de dire "trainingComplete" qui prend l'une des Enums en entrée et l'enregistre dans un ensemble. Donc

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

Cependant, au lieu de définir la 'terminé' dans chacune des Formateurs et le "trainingComplete' méthode dans chacun des formateurs, j'aimerais quelque chose dans le parent " Formateurs de la classe Enum type forcée... Donc c'est un mélange étrange de les Énumérations et les génériques.

Est-ce possible?

28voto

Bohemian Points 134107

Pour spécifier un lié à une interface et d'un enum, vous avez besoin d'un générique d'intersection, qui ressemble à:

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

Notez que lorsque l'intersection d'une classe et une interface(s), la classe doit apparaître avant que l'interface(s).

Dans ce cas, les produits génériques de Kung Fu que vous voulez est d'un niveau plus complexe, car l'interface de la TrainingActions enum doit se référer au type d'animal.

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

Un travail complet exemple, en fonction de votre posté code qui compile est:

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

Mais je voudrais aller plus loin et de définir plusieurs TrainingActions énumérations dans la classe de l' Animal auquel ils s'appliquent:

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

1voto

killer_PL Points 1821

En dépit de l'analyse de la conception, je pense que le plus à votre situation (le plus proche de votre déjà créé conception) la réponse est:

  • votre DogTrainer.Trainables est TrainingActions<Dog>
  • votre HorseTrainer.Trainables est TrainingActions<Horse>

Généralement, votre Trainables est déjà un TrainingActions<T extends Animal>

ainsi, vous pouvez simplement fournir de l' Set<TrainingActions<T>> completed, parce que vous avez déjà à votre animal de renseignements en T:

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

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

Et vous avez exactement ce que vous voulez.

Utilisation:

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

Si vous êtes sûr que vous voulez appliquer un Enum pour votre interface:

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

1voto

killer_PL Points 1821

Je vais poster que pour la poursuite de l'examen, même il y a déjà accepté de répondre à

Si vous lisez cette question, pensez à utiliser de plus lâche couplé éléments, à l'agrégation, etc. au lieu de très spécifique et complexe de type hiérarchie et les génériques.

Avec de la simple agrégation que vous pouvez atteindre au moins le même type et la logique de la sécurité, mais lâche couplé design. Comme je l'ai mentionné dans les commentaires:

Sans doute, meilleure sera plus collections, agregations etc. pour assurer type de sécurité et elasticyty ensemble (différents formateurs, avec différents les capacités de différents animaux dans toutes les combinaisons, y compris le mélange, pas de verrouillage d'une personne d'être un super-spécifiques maître de stage pour certains super capacités spécifiques de l'animal et rien de plus).

Bien sûr, je veux dire un "Jean que peut enseigner le cheval au trot et un chien l'écorce", non pas "Jean qui peuvent enseigner les chevaux et les chiens de trot ou de l'écorce"

-2voto

Prabhakaran Points 10003

Oui, vous pouvez déplacer votre Trainables , Set<Trainables> completed et trainingComplete(Trainables t) de l'Entraîneur de classe.

De cette façon, vous n'avez besoin d'écrire de code à chaque DogTrainer, HorseTrainer .. et ainsi de suite...

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

Sinon faire trainingComplete(Trainables t) résumé et vous avez besoin de mettre en œuvre dans chaque Formateur implementaion clasees.

abstract class Trainer<T extends Animal>{
     public enum Trainables implements TrainingActions<Animal>{
            BARK, BITE, ROLLOVER, FETCH;
     }
      public Set<Trainables> completed = new HashSet<Trainables>();
      abstract public void trainingComplete(Trainables t);
}

Maintenant, votre DogTrainer et HorseTrainer sont la mise en œuvre de trainingComplete(Trainables t)

 public class DogTrainer extends Trainer<Dog>{      
     public void trainingComplete(Trainables t){
          completed.add(t);
     }
 }

 public class HorseTrainer extends Trainer<Horse>{     
     public void trainingComplete(Trainables t){
           completed.add(t);
     }
 }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X