4

If I have a bunch of classes that all contain an Enum and EnumMap and I want to create a superclass for those classes.

public interface ColorEnum {
}

class ColorMarbles extends Toy {
    enum MARBLE implements ColorEnum
        { BLUE, GREEN }
    EnumMap<MARBLE, String> names = new EnumMap<MARBLE, String>(MARBLE.class);
    //stuff
    // fields

    public void populate(ArrayList<String> designer) {
        int i = 0;
        for(MARBLE marble : MARBLE.values()) {
            marble.name = designer.get(i);
            i++;
        }
    }
}

class ColorBalloons extends Toy {
    enum BALLOON implements ColorEnum
        { YELLOW, RED }
    EnumMap<BALLOON, String> names = new EnumMap<BALLOON, String>(BALLOON.class);
    //stuff
    // fields

    public void populate(ArrayList<String> designer) {
        int i = 0;
        for(BALLOON balloon : BALLOON.values()) {
            balloon.name = designer.get(i);
            i++;
        }
    }
}

How do I make create a superclass to have a generic EnumMap that contains an enum of type ColorEnum like this?

public abstract class Toy {
    EnumMap<ColorEnum, String> names;
}

eidt: I realize that I was too vague with my example. Dogs are probably a bad example. I change it to something hopefully more clear.

What I have is a bunch of classes with methods like populate which populates the EnumMap. The names are in a predefined order. Instead of defining populate in every class, I'm hoping to be able to bring it to the Toy superclass so I don't have to keep copy-pasting in each new class type Toy.

Hopefully this will explain more what I'm looking for.

5
  • I'm not sure what you are trying to do. Can you clear it up a little? Commented Feb 20, 2013 at 16:13
  • Why not simply using a HashMap ? EnumMap is just an optimized implementation for enums. Commented Feb 20, 2013 at 16:15
  • I still can't clearly see the intent here. Where and by whom are actually called the populate() methods? Why can't they simply be static? Are you expecting to call them multiple times at runtime with different values (thus changing the names)? Is the designer list the same for the marbles and balloons, meaning that blue marble and yellow balloon have the same name or are they completely unrelated? Sorry for so much questions, but I still can't see why the simple enum approach can't work here. :) Commented Feb 20, 2013 at 17:09
  • @Natix I call populate() multiple times so the fields cannot be static, like in a list of Balloons, each with different designers. Commented Feb 20, 2013 at 17:39
  • @JustinYang And do you actually need enums at all? Can't you just create simple a collection, such as List<Balloon> balloons = new ArrayList<>(); and manually fill it with Balloon objects with the required names? Commented Feb 21, 2013 at 10:30

2 Answers 2

5

I have a feeling your design is needlessly overcomplicated.

With enums

If you don't require a class inheritance, you can work with enums directly as with top level classes.

public interface Animal {}

public enum Dog implements Animal {
    HUSKY("Husky"), LAB("Labrador");

    private final String name;

    Dog(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

Enums can declare fields, methods and implement interfaces like any other Java classes. Their only limitation is that their direct superclass is always java.lang.Enum and they can't be extended.

However every enum constant can have its own set of unique data passed to its constructor. It is even possible that each of the constants can override a common method of that enum with its unique implementation.

A nice tutorial explaining more about the full power of enums: http://javarevisited.blogspot.cz/2011/08/enum-in-java-example-tutorial.html


Without enums

In case you need an actual class inheritance for sharing some common methods (for example from the Animal superclass), I still would drop the map approach and rather try something more OOP oriented:

public class Animal {
}

public abstract class Dog extends Animal {

    public abstract String getName();

    public static class Husky extends Dog {
        @Override
        public String getName() {
            return "husky";
        }
    }

    public static class Lab extends Dog {
        @Override
        public String getName() {
            return "labrador";
        }
    }

}
Sign up to request clarification or add additional context in comments.

4 Comments

You might also push getName() up to the interface.
I have methods in each class that do the exact same thing and to slim down all the classes I want to create an abstract superclass that can house all the generic methods. But to do that I need access to the EnumMap.
Maybe I am not actually helping with your problem, but this is all that I could come up with from your example with dog names. Maybe you should make your question more specific about what you are trying to achieve.
@Natix thanks for your replies, I realize my example was pretty bad. I've changed it up so hopefully it is clearer
3

One mechanism I have used for something like this is to extend a generic base class that has a generic parameter that allows you to pass the Enum details up to it.

This example defines a base Table class for database tables:

public class Table<Column extends Enum<? extends Column>> {
  // Name of the table.
  protected final String tableName;
  // All of the columns in the table. This is actually an EnumSet so very efficient.
  protected final Set<Column> columns;

  /**
   * The base interface for all Column enums.
   */
  public interface Columns {
    // What type does it have in the database?
    public Type getType();
  }

  // Small list of database types.
  public enum Type {
    String, Number, Date;
  }

  public Table(String tableName,
               Set<Column> columns) {
    this.tableName = tableName;
    this.columns = columns;
  }

}

Now you can subclass this:

public class VersionTable extends Table<VersionTable.Column> {

  public enum Column implements Table.Columns {
    Version(Table.Type.String),
    ReleaseDate(Table.Type.Date);

    // Sadly all of this must be in ALL of your enums but most of the work can be pushed up to `Table`
    final Table.Type type;

    Column(Table.Type type) {
      this.type = type;
    }

    @Override
    public Type getType() {
      return type;
    }
  }

  public VersionTable() {
    super("Versions", EnumSet.allOf(Column.class));
  }
}

and make use of functionality in the parent class that handles your enum.

Note here I am passing an EnumSet to the Table constructor. I am sure you could change this to accommodate your EnumMap requirement if you decide an EnumSet is insufficient.

2 Comments

A clever pattern, but I am a bit concerned about the type naming: Columns is a nested interface in Table where Column is a type variable defined in the scope of the generic Table class. I would change the declaration to class Table<C extends Enum<? extends C>> to avoid confusion.
@Natix - I though long and hard about the naming and did consider the current method of one-letter capitals for generic types. By all means choose whatever naming convention you like. In my environment where I use about a dozen tables all defined like this the Columns/Column reads the most naturally for me and there is no confusion. Obviously you see the huge benefit of strong type checking while still being able to write most of the boiler-plate code just once.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.