简体   繁体   English

在Java中将泛型类型与Enum相关联

[英]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. 我希望能够做的是存储一个带有名称的值类型,以便服务将使用正确的Java类型存储用户的值。 For example, FOO might be a Long , and BAR might be a String . 例如,FOO可能是Long ,BAR可能是String Up until now, we were storing all values as String s, and then manually casting the values into the appropriate Java type. 到目前为止,我们将所有值存储为String ,然后手动将值转换为适当的Java类型。 This has lead to try/catch blocks everywhere, when it makes more sense to have only one try/catch in the service. 这导致了无处不在的try / catch块,当在服务中只有一次try / catch更有意义时。 I understand that Enums cannot have generic types, so I have been playing around with: 我知道Enums不能有通用类型,所以我一直在玩:

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. 我有一个通用的UserSetting对象,它具有public T getSettingValue()public void setSettingValue(T value)方法,它们应返回并使用正确的类型设置值。 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: 我的问题来自于在创建或检索设置时尝试指定泛型类型T ,因为我无法执行以下操作:

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 UPDATE

Both the setting name and value are coming in from a Spring MVC REST call: 设置名称和值都来自Spring MVC REST调用:

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. 所以我使用了Enum,因为Spring会自动转换传入的数据。

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. 目前还不完全清楚你要追求的是什么,但从你的方法来看,几乎可以肯定你正在重新发明一些可以用更直接的方式在Java中完成的事情。 For example, if you really need to know and work with the runtime classes of objects, consider using the reflection API. 例如,如果您确实需要了解并使用对象的运行时类,请考虑使用反射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. 泛型是一种编译时语言特性 - 它们有助于避免从Object显式地转换所有内容,并在编译时为您提供类型检查。 You simply cannot use generics in this way, ie setting T as some value UserSettingName.Foo.getType() which is only known at runtime. 你不能以这种方式使用泛型,即将T设置为某个值UserSettingName.Foo.getType() ,它只在运行时已知。

Look how it done by netty: 看看它如何通过netty完成:

http://netty.io/wiki/new-and-noteworthy.html#type-safe-channeloption 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 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: EDIT2:你可以改变它:

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

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. 如果你发现自己在任何地方重复代码,你可以创建一个实用程序类并在那里封装所有的try / catch逻辑。 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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM