Marcin Nowak
Marcin Nowak

Reputation: 77

Map with enum key and different value types

I want to define map in Java, which keys are enums, and types of value depend of key. For example, suppose that we have following enum type:

enum KeyType {
        HEIGHT(Integer.class),
        NAME(String.class),
        WEIGHT(Double.class)
       // constructor and getter for Class field

}

and some map:

Map< KeyType, Object > map = new EnumMap<>(KeyType.class);

Is there any simple and safe way to write generic method:

public < T > T get(KeyType key) {
//...
}

that would get value from that map and cast it to corresponding with type class?

Upvotes: 5

Views: 3395

Answers (3)

VGR
VGR

Reputation: 44318

You can't do it with enums. But you could write a "fake" enum (the way Java code did it before Java 1.5, with private constructors and public static instances), and attach a generic type to each constant:

import java.io.Serializable;
import java.util.Map;

public final class KeyType<T>
implements Serializable {
    private static final long serialVersionUID = 1;

    public static final KeyType<Integer> HEIGHT =
        new KeyType<>("HEIGHT", Integer.class);

    public static final KeyType<String> NAME =
        new KeyType<>("NAME", String.class);

    public static final KeyType<Double> WEIGHT =
        new KeyType<>("WEIGHT", Double.class);

    private static final KeyType<?>[] allValues = {
        HEIGHT, NAME, WEIGHT
    };

    /** @serial */
    private final String name;

    /** @serial */
    private final Class<T> type;

    private KeyType(String name,
                    Class<T> type) {
        this.name = name;
        this.type = type;
    }

    public String name() {
        return name;
    }

    public Class<T> getType() {
        return type;
    }

    @Override
    public String toString() {
        return name();
    }

    public static KeyType<?>[] values() {
        return allValues.clone();
    }

    public static KeyType<?> valueOf(String name) {
        for (KeyType<?> value : allValues) {
            if (value.name.equals(name)) {
                return value;
            }
        }
        throw new IllegalArgumentException("No such value: \"" + name + "\"");
    }

    @Override
    public boolean equals(Object obj) {
        return (obj instanceof KeyType &&
            this.name.equals(((KeyType<?>) obj).name));
    }

    @Override
    public int hashCode() {
        return name.hashCode();
    }

    public T getValue(Map<KeyType<?>, ?> map) {
        return type.cast(map.get(this));
    }
}

Upvotes: 0

xerx593
xerx593

Reputation: 13261

UPDATE!!!: With this in mind:

enum KeyType {

    //your enums ...
    private final Class val;

    //constructor ...

    //and generic(!) access to the class field:
    <T> Class<T> val() {
        return val;
    }
}

...this is possible:

public <T> T get(KeyType key) {
    return (T) key.val().cast(map.get(key));
}

Upvotes: 2

MadConan
MadConan

Reputation: 3767

Your map definition would need to be

Map< KeyType, ?> map = new EnumMap<>(KeyType.class);

If you specify Object as a generic type, only actual instances of Object are allowed, not sub-types.

I don't believe there's any straight forward, generic way (no pun intended) to do what you want. You would need to create some mapping function that translates the object to the correct type based on the enum.

Upvotes: 0

Related Questions