Binding a generic type to Enum in Java

I am creating a repository for user preferences, and there is a fixed number of preferences that users can set for values. Names of settings (settings) are saved as Enum:

public enum UserSettingName { FOO, BAR, ETC } 

What I would like to do is save the value type with a name so that the service saves the user value with the correct Java type. For example, FOO may be Long , and BAR may be String . So far, we have saved all the values ​​as String s, and then manually entered the values ​​into the appropriate Java type. This led to the fact that try / catch blocks are used everywhere, when it makes sense to have only one try / catch in the service. I understand that Enums cannot have common types, so I played 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 common UserSetting object that has the public T getSettingValue() and public void setSettingValue(T value) methods that should return and set the value with the correct type. My problem is trying to specify this generic type T when creating or retrieving a parameter, because I cannot do something like:

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

Sorry, if this is not entirely clear, I can try to find out if he did not understand.

Thanks!

UPDATE

Both parameter names and values ​​are included in the Spring MVC REST call:

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

So, I used Enum because Spring automatically inputs the input.

+6
source share
3 answers

First, you need to step back and think about what you are trying to achieve, and use a standard model or language construct to achieve it.

It's not entirely clear what you are doing here, but from your approach it almost certainly looks like you are inventing something that can be done much more straightforwardly in Java. For example, if you really need to know and work with runtime classes, consider using the reflection API.

On a more practical level, what you are trying to do here is not possible with generics. Generics is a language function for compilation - they are useful in order to avoid the explicit exclusion of all objects from Object and to give you type checking at compile time. You simply cannot use generics in this way, that is, set T as some value of UserSettingName.Foo.getType() , which is known only at runtime.

+3
source

See how netty does it:

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

They did this 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 convert it like this:

 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()); } } 
+2
source

Enumerations here are not the answer. If you repeat the code everywhere, you can simply create a utility class and encapsulate all the try / catch logic. This would reduce code redundancy without significantly affecting your current code.

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

Then use the following:

 MyObject myObj = Util.getObjectFromString(string); 
0
source

Source: https://habr.com/ru/post/955943/


All Articles