Ben March
Ben March

Reputation: 566

Associating a generic type with Enum in Java

I am creating a store for user preferences, and there are a fixed number of preferences that users can set values for. The names of the preferences (settings) are stored as an Enum:

public enum UserSettingName {

    FOO,
    BAR,
    ETC

}

What I would like to be able to do is store a value type with the name so that the service will store the user's value with the correct Java type. For example, FOO might be a Long, and BAR might be a String. Up until now, we were storing all values as Strings, and then manually casting the values into the appropriate Java type. This has lead to try/catch blocks everywhere, when it makes more sense to have only one try/catch in the service. I understand that Enums cannot have generic types, so I have been playing around with:

public enum UserSettingName {

    FOO(Long.class),
    BAR(String.class),
    ETC(Baz.class)

    private Class type;

    private UserSettingName(Class type) {
        this.type = type;
    }

    public Class getType() {
        return this.type;
    }
}

I have a generic UserSetting object that has public T getSettingValue() and public void setSettingValue(T value) methods that should return and set the value with the correct type. My problem comes from trying to specify that generic type T when I create or retrieve a setting because I can't do something like:

new UserSetting<UserSettingName.FOO.getType()>(UserSettingName.FOO, 123L)

Sorry if this isn't exactly clear, I can try to clarify if it's not understood.

Thanks!

UPDATE

Both the setting name and value are coming in from a Spring MVC REST call:

public ResponseEntity<String> save(@PathVariable Long userId, @PathVariable UserSettingName settingName, @RequestBody String settingValue)

So I used the Enum because Spring casts the incoming data automatically.

Upvotes: 6

Views: 3994

Answers (3)

Anton Bessonov
Anton Bessonov

Reputation: 9813

Look how it done by netty:

http://netty.io/wiki/new-and-noteworthy.html#type-safe-channeloption

They done it by using typed constants:

http://grepcode.com/file/repo1.maven.org/maven2/io.netty/netty-all/4.0.0.Beta1/io/netty/channel/ChannelOption.java#ChannelOption

EDIT:

public interface ChannelConfig {
   ...
   <T> boolean setOption(ChannelOption<T> option, T value);
   ...
}

public class ChannelOption<T> ...
    public static final ChannelOption<Integer> SO_TIMEOUT =
        new ChannelOption<Integer>("SO_TIMEOUT");
    ...
}

EDIT2: you can transform it like:

class Baz {}

class UserSettingName<T> {
    public static final UserSettingName<Baz> ETC = new UserSettingName<Baz>();
}

class UserSetting {
    public <T> UserSetting(UserSettingName<T> name, T param) {

    }
}

public class Test {
    public static void main(String[] args) {
        new UserSetting(UserSettingName.ETC, new Baz());
    }
}

Upvotes: 2

aglassman
aglassman

Reputation: 2653

Enums are not the answer here. If you find yourself repeating code everywhere you could just create a utility class and encapsulate all the try/catch logic there. That would cut down on your code redundancy without majorly impacting your current code.

public class Util
{
    public static MyObject getObjectFromString(String s)
    {
        try
        {
            return (MyObject)s;
        }
        catch(Exception e)
        {
            return null;
        }
    }
}

Then use as follows:

MyObject myObj = Util.getObjectFromString(string);

Upvotes: 0

davnicwil
davnicwil

Reputation: 30957

Firstly you have to step back and think about what you're trying to achieve, and use a standard pattern or language construct to achieve it.

It's not entirely clear what you're going after here but from your approach it almost certainly looks like you're reinventing something which could be done in a much more straightforward manner in Java. For example, if you really need to know and work with the runtime classes of objects, consider using the reflection API.

On a more practical level - what you're trying to do here isn't possible with generics. Generics are a compile-time language feature - they are useful for avoiding casting everything explicitly from Object and give you type-checking at compilation time. You simply cannot use generics in this way, i.e. setting T as some value UserSettingName.Foo.getType() which is only known at runtime.

Upvotes: 3

Related Questions