anishthecoder
anishthecoder

Reputation: 940

"Sets" of a particular enum type with nested generics

This is somewhat of a followup from a previous question I had.

Let's say I have an abstract parent class for animals. The same kind of animal can have different temperaments and can perform in different types of shows. So my animal definition looks something like this:

public abstract class Animal<T extends Temperament, S extends Show>{...}

I want to have trainers for various animals that are aware of the animal type, temperament, intended type of shows the animal is to perform in, AND defines a set of tricks the trainer can teach that animal. Because I want the set of tricks to be defined for a particular animal, I have an enum interface as follows:

public interface TrainingActions<T extends Animal<?,?>>{...}

Any enum that implements that interface defines a set of training actions for a particular animal regardless of its temperament and the shows it can perform in.

Keeping these in mind, my definition for the parent class of trainers is as follows:

public abstract class Trainer
  <A extends Animal<?,?>, 
   E extends Enum<E> & TrainingActions<A>,
   T extends Temperament,
   S extends Show>{
...}

Now, I was trying to create a concrete trainer as follows but get an error:

public class DogTrainer
  <T extends Temperament,
   S extends Show> extends Trainer
     <Dog<T,S>, DogTrainer.Trainables, T, S>{//error right here 

  public enum Trainables implements TrainingActions<Dog<?,?>>{
     FETCH, GROWL, SIT, HEEL;
  }
  ...
}

I'm getting the following error with trying to use DogTrainer.Trainables as the parameters for Trainer in my definition of DogTrainer:

Bound mismatch: The type DogTrainer.Trainables is not a valid substitute 
for the bounded parameter <E extends Enum<E> & TrainingActions<A>> of the type 
Trainer<A,E,T,S>

Can someone help me understand what I'm doing wrong?

Upvotes: 1

Views: 92

Answers (2)

Holger
Holger

Reputation: 298203

You have created an unsolvable problem by adding to much constraints into you generic signatures. Your current solution is too relaxed compared with your (assumed) intention. Look at the following declaration:

public abstract class Trainer
  <A extends Animal<?,?>, // <- here you are not enforcing the right bounds
   E extends Enum<E> & TrainingActions<A>,
   T extends Temperament,
   S extends Show>{
  …
}

With this declaration you allow to pass an animal with the wrong Temperament and Show to a particular Trainer instance. What you most probably want is:

public abstract class Trainer
<A extends Animal<T,S>, 
 E extends Enum<E> & TrainingActions<A>,
 T extends Temperament,
 S extends Show> {
 …
}

But then, your solution does not work anymore. With this declaration you cannot create a DogTrainer having type parameters for Temperament and Show without declaring the trained animal as Dog<T,S> as it is intended since a Trainer ought to train only animals with the right Temperament and Show (otherwise you don’t need to make them type parameters).

But if you declare the DogTrainer as DogTrainer<T extends Temperament, S extends Show> extends Trainer<Dog<T,S>, DogTrainer.Trainables, T, S> you cannot specify your intended TrainingAction as it does not have the right Temperament and Show and this is impossible to fix because you want the TrainingAction to be an enum. And enums can only be declared either with wildcards like you did with enum Trainables implements TrainingActions<Dog<?,?>> or with exactly one specific type. Inner enums are always static and cannot refer to the outer classes type parameters.

So this is the combination of constraints summarized:

  1. The Trainer has the parameters animal A and temperament T and show S and training action E
  2. A should have the right T and S for a trainer
  3. E should have the right A for the trainer
  4. E shall be an enum
  5. You want different trainers with different types for T and S but a constant type for E (as it is an enum)

This is impossible. You current solution ignores the constraint 2 but there’s no solution without removing at least one of the constrains

Upvotes: 1

anishthecoder
anishthecoder

Reputation: 940

Figured out the problem. My concrete trainer needed to be defined as follows:

public class DogTrainer
  <T extends Temperament,
   S extends Show> extends Trainer
     <Dog<?,?>, DogTrainer.Trainables, T, S>{// changed line

  public enum Trainables implements TrainingActions<Dog<?,?>>{
     FETCH, GROWL, SIT, HEEL;
  }
  ...
}

Upvotes: 0

Related Questions