【问题标题】:Android enum reflectionAndroid枚举反射
【发布时间】:2016-07-13 09:02:56
【问题描述】:

过去几个小时我一直在尝试使以下功能正常工作。

public class OMG {

    String name;
    HashMap<SETTINGS, String> settings;

    public enum SETTINGS {
        Setting1("OMG setting 1"), Setting2("OMG setting 2");
        private String key;

        @Override
        public String toString() {
            return "SETTINGS: " + key;
        }

        SETTINGS(String key){
            this.key = key;
        }
    }

    public OMG(String name, HashMap<SETTINGS, String> settings) {
        this.name = name;
        this.settings = settings;
    }
}

public class Test {

    public static void main(String[] args) {
        try {
            Class<?> c = Class.forName("path.to.OMG" + "$" + "SETTINGS");

            System.out.println(Arrays.toString(c.getEnumConstants()));
            HashMap<c,String > values = new HashMap<>();
            OMG omg = new OMG("blah",values);

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

我必须创建一个 OMG 实例并获得 OMG 的路径和枚举名称。到目前为止,我在 Test 类中有代码。我能够获取所有 enumValues,但是我不知道如何创建 HashMap 以及如何使用此 HashMap 调用构造函数来获取 OMG 类的实例。

请帮忙。

谢谢

【问题讨论】:

  • 您确定需要使用枚举名称吗?为什么不能只创建一个HashMap&lt;SETTINGS, String&gt;?此外,OMG 类应该与任何枚举一起使用还是仅与 SETTINGS 枚举一起使用?
  • 因为现实中的OMG类有多个枚举,我需要传递多个字典
  • 那么你的问题解决了吗?如果不是,请发布代码示例和问题描述,以便我们提供有用的答案。如果是,请通过接受最有帮助的答案来表明这一点。

标签: java android reflection enums


【解决方案1】:

没有实际理由去做你想做的事。实例化OMG 的方法是new OMG("foo", new HashMap&lt;SETTINGS, String&gt;());

但是,作为一个纯粹的学术练习(我喜欢这些!),让我们看看我们是否可以仅使用作为字符串给出的类名来实例化OMG,而无需在任何地方实际输入OMGSETTINGS。可以的!

在 Java 中,泛型只是一个编译时特性。在运行时,HashMap&lt;SETTINGS, String&gt;HashMap&lt;Double, Float&gt; 或原始HashMap 没有区别。因此,您可以实例化任何旧的 HashMap - 根本不需要使用 c 对象(无论如何,您都不能使用 Class 对象作为类型参数)。

这里有一个解决方案:

public static void main(String[] args) throws Exception {
    Class<?> clazz = Class.forName("path.to.OMG");
    Constructor constructor = clazz.getConstructor(String.class, HashMap.class);
    Object omgInstance = constructor.newInstance("foo", new HashMap<Void, Void>());
    System.out.println(omgInstance.getClass());    // OMG - it worked!
}

【讨论】:

  • 你的第三段让我思考并基本上解决了我的问题。谢谢。
  • 很高兴能帮上忙。
【解决方案2】:

从您自己的回答中,我了解到您希望将枚举条目用作键和默认值,用于存储在类 OMG 的映射中的实际配置。此配置也应写入文件。

现在,您必须做出设计决定:您希望何时能够更改可以使用的配置键和/或默认值?当程序已经在运行时(在运行时)或当程序重新编译时(在编译时)?

您的描述表明您希望能够在运行时更改它们。它是否正确?另外,这真的需要吗?您现有的代码可能不会使用为新键提供的值。此外,更改的默认值可能不会产生影响,因为该值已经使用旧的默认值进行了初始化。

如果您真的想在运行时更改这些信息,那么您不应该使用枚举来存储和加载它们。虽然这种编程风格可能适用于 JavaScript 等脚本语言,但它不适用于 Java。枚举,与任何其他代码一样,不应在运行时更改,但应在编译时给出。

由于在 Java 中代码不应该在运行时更改,JVM 甚至不允许简单地重新加载一个类,而无需执行一些类加载器魔法。您通常希望尽可能避免这种情况。有关这方面的更多信息,请参阅here。在运行时重新加载类并非不可能,但对于您所面临的问题,这绝对是错误的解决方案。

那么,什么是正确的解决方案?我仍然不相信您真的需要在运行时更改信息。如果这是正确的并且您只需要在编译时更改信息,则可以简单地使用当前的枚举方法。但是,您可以将其简化为不按名称加载类,有关可能实现的更多详细信息,请参见下文。

如果您确实需要能够在运行时更改信息,则不应将信息存储在代码中,而应存储在文件中。这使您可以通过重新读取文件轻松地重新加载信息。您可以使用 Java 世界中常见的属性文件,请参阅here。如果您需要更结构化的文件格式,可以使用 JSON 文件。在 Java 中,您可以使用 Gson 处理 JSON 文件,请参阅 here


这对我有用:

package test;

import java.util.Arrays;
import java.util.EnumMap;

public class Test {
    public static enum SETTINGS {
        Setting1("OMG setting 1"), Setting2("OMG setting 2");

        private String key;

        @Override
        public String toString() {
            return "SETTINGS: " + key;
        }

        SETTINGS(String key) {
            this.key = key;
        }
    }

    public static class OMG<E extends Enum<E>> {
        String name;
        EnumMap<E, String> settings;

        public OMG(String name, EnumMap<E, String> settings) {
            this.name = name;
            this.settings = settings;
        }

        public static <E extends Enum<E>> OMG<E> create(String name,
                Class<E> enumClass) {
            return new OMG<E>(name, new EnumMap<E, String>(enumClass));
        }
    }

    public static void main(String[] args) {
        try {
            Class c = Class.forName("test.Test$SETTINGS");
            // alternatively you can use:
            // Class<SETTINGS> c = SETTINGS.class;

            System.out.println(Arrays.toString(c.getEnumConstants()));
            OMG.create("foobar", c);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

我假设您想将任何枚举类型传递给 OMG 类,而不仅仅是 SETTINGS。如果它应该只是SETTINGS,你还不如直接在你的代码中使用SETTINGS。这就是我将 SETTINGS 枚举移出 OMG 类的原因。

接下来,我在OMG类中添加了一个泛型类型参数E,所以OMG不直接依赖SETTINGS。为了使初始化更容易,我在OMG 中添加了一个静态工厂方法,它也使用了一个泛型类型参数E。它只接收与E 匹配的名称和类对象。您需要类对象,因为在 Java 中您不能直接使用泛型类型参数进行反射。

另外,我将HashMap 替换为EnumMap,因为EnumMap 是当键是枚举时优化的映射实现。

最后,OMG类的初始化:如果确实需要从类名中创建类对象,使用反射,直接使用代码即可。请注意,这将产生警告。然而,大多数时候你想直接使用类对象,就像在注释的代码行中一样。如果您使用第二种方法,您将不会收到警告,您也可以省略 try-catch 块。

【讨论】:

    【解决方案3】:

    感谢@PaulBoddington,我解决了以下问题。

    public class Test {
    
        public static void main(String[] args) {
            try {
                Class<?> c = Class.forName("path.to.OMG" + "$" + "SETTINGS");
                Object[] enumConstants = c.getEnumConstants();
                HashMap<Object, String > configuration = new HashMap<>();
                for (Object enumConstant: enumConstants){
                  configuration.put(enumConstant, enumConstant.key);
                }
                OMG omg = new OMG("blah", configuration);
    
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    

    人们想知道的用例是 OMG 是系统中的一个模块,它是可配置的,并且还必须允许从开发人员的角度轻松添加新设置。这就是为什么设置及其值保存在 HashMap 中的原因,而 SETTING 是一个枚举,其中包含配置文件中的键,在该配置文件中应该找到设置以及其他信息 - 例如的默认值。 OMG 类也有一个

    String getSettingValue(SETTING setting){
        return settingsMap.get(setting);
    }
    

    返回设置值并由模块公开的方法。

    这意味着如果必须将新设置添加到组件中,我只需创建 SETTINGS 枚举的新实例即可。在创建过程中,如果服务器没有为此设置发送值,我可以使用默认值或崩溃。另一方面,一旦组件被实例化,我就可以立即访问新设置,而无需触及现有代码。

    请分享您对这个架构的看法,因为它是针对学校项目的,我非常怀疑老师是否会真正研究它并告诉我它是否合理。

    谢谢

    【讨论】:

    • 我扩展了我的答案以反映此答案中的信息。这里还有几句话:这个答案中的代码是否编译? enumConstant.key 应该是无效的,因为 Object 没有定义 .key。接下来,您不能在运行时创建新的枚举条目。此外,您不能轻松地重新加载枚举。您正在使用枚举来存储和加载信息。只要您不想在运行时更改信息就可以了。更多信息,请看我的回答。
    猜你喜欢
    • 1970-01-01
    • 2011-06-12
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 2012-12-12
    • 1970-01-01
    相关资源
    最近更新 更多