Woodrow Barlow
Woodrow Barlow

Reputation: 9047

Create enum with entries associated to classes

Supposing I have several specific classes which all extend one abstract class, like so:

public abstract AbstractClass {
  // abstract stuff here
}

public FirstSpecificClass extends AbstractClass {
  // specific stuff here
}

public SecondSpecificClass extends AbstractClass {
  // specific stuff here
}

I need to create an enum elsewhere in which each entry is connected (associated?) with one of the specific classes; to that end, I am passing the specific class as a constructor parameter and storing it as a private field within the enum (I've also provided a getter method for that field). I also need to create a static method which takes an instance of one of the specific classes as an argument and returns the appropriate enum element (or null). I will do this by looping over each enum entry and using instanceof in combination with the getter for the private field mentioned previously. This is my attempt:

public enum Types {
  FIRST(FirstSpecificClass.class),   // line 2
  SECOND(SecondSpecificClass.class); // line 3

  private Class<AbstractClass> classType;

  private Types(Class<AbstractClass> classType) {
    this.classType = classType;
  }

  public Class<AbstractClass> getClassType() {
    return this.classType;
  }

  public static Types fromTypeInstance(AbstractClass instance) {
    for(Types t : Types.values())
      if(instance instanceof t.getClassType()) return t; // line 17
    return null;
  }
}

I seem to misunderstanding how to store the class type as a field so that it can be returned and used in the instanceof check later. This code is producing several compile-time errors:

I am not usually a Java programmer, and my understanding of generics and instanceof is fuzzy at best, although I do have a pretty firm grasp on the concept of OOP. How can I resolve these errors and achieve the desired effect?

Upvotes: 3

Views: 847

Answers (1)

rgettman
rgettman

Reputation: 178253

In Java, generics are invariant. This means that a Class<FirstSpecificClass> is not a Class<AbstractClass>, even if a FirstSpecificClass is an AbstractClass.

You can work around this by explicitly allowing subtypes with an upper bound wildcard. Add ? extends before your AbstractClass type argument where needed.

private Class<? extends AbstractClass> classType;

private Types(Class<? extends AbstractClass> classType) {
   this.classType = classType;
}

public Class<? extends AbstractClass> getClassType() {
   return this.classType;
}

Additionally, you must specify the type directly in the source code for the instanceof operator, so this doesn't compile:

if(instance instanceof t.getClassType())

You can use the Class object's isInstance method instead, for a runtime solution:

if(t.getClassType().isInstance(instance))

Upvotes: 5

Related Questions