jabalsad
jabalsad

Reputation: 2421

Many enums with same functionality?

I'd like to create a few enums such as described in this answer: Lookup enum by string value

(I.e an enum that has a toString() and a name which is different to the enum itself)

Is there any way I can reuse this functionality without having to reimplement it in each enum?

Upvotes: 1

Views: 159

Answers (3)

Tom Hawtin - tackline
Tom Hawtin - tackline

Reputation: 147154

If you pass in the list of possible values, then you can do it without so much as depending upon the type being an enum.

public static <T> T findByString(T[] values, String toString) {
    Objects.requireNonNull(toString);
    for (T t : values) {
        if (toString.equals(t.toString())) {
            return t;
        }
    }
    throw new IllegalArgumentException();
}

Call with:

Blah blah = SomeClass.findByString(Blah.values(), str);

Rather than calling Blah.values() every time, you could use put it in an object, which could be passed around and used generically. Perhaps other methods added later.

public interface ByString<T> { // Choose a better name.
    T find(String toString);
}
[...]
    private static final ByString<Blah> blahs = byString(Blah.values());
[...]
    public static <T> ByString<T> byString(T[] values) {
        final T[] valuesCopy = values.clone();
        return new ByString<T>() {
            public T find(String toString) {
                return SomeClass.findByString(valuesCopy, toString);
            }
        };
    }
[...]

Call with:

Blah blah = blahs.find(str);

In fact, you might want to optimise that. Do the toStringing once, and use a map.

    public static <T> ByString<T> byString(T[] values) {
        final Map<String,T> byToStrings = new HashMap<>(
            (int)(values.length/0.75f)+1 // Doesn't HashMap construction suck.
        );
        for (T value : values) {
            byToStrings.put(values.toString(), value);
        }
        return new ByString<T>() {
            public T find(String toString) {
                T value = byToStrings.get(toString);
                if (value == null) {
                    throw new IllegalArgumentException();
                }
                return value;
            }
        };
    }

Upvotes: 3

Peter Lawrey
Peter Lawrey

Reputation: 533492

If you have many enums which share the same functionality I would either.

  • Use one enum since they have a lot in common.
  • Use a helper method each enum can call.

Java 8 will make this simpler with virtual extensions. It will allow you to define a default implementation on the interface (which has to be a helper/static method)

Upvotes: 1

Jon Skeet
Jon Skeet

Reputation: 1500365

I don't believe you can get away from the "adding a field and a property" in each enum. You can make your fromText method delegate to a common method though, if you create an interface for that property, and pass either values() or (better) an EnumSet. (Better: write a method to create a case-insensitive Map, and call that once in each enum, storing the result in a static variable.)

Upvotes: 3

Related Questions