枚举中的可配置值
我经常在我的代码中使用这个设计来维护可配置的值。 考虑以下代码:
public enum Options { REGEX_STRING("Some Regex"), REGEX_PATTERN(Pattern.compile(REGEX_STRING.getString()), false), THREAD_COUNT(2), OPTIONS_PATH("options.config", false), DEBUG(true), ALWAYS_SAVE_OPTIONS(true), THREAD_WAIT_MILLIS(1000); Object value; boolean saveValue = true; private Options(Object value) { this.value = value; } private Options(Object value, boolean saveValue) { this.value = value; this.saveValue = saveValue; } public void setValue(Object value) { this.value = value; } public Object getValue() { return value; } public String getString() { return value.toString(); } public boolean getBoolean() { Boolean booleanValue = (value instanceof Boolean) ? (Boolean) value : null; if (value == null) { try { booleanValue = Boolean.valueOf(value.toString()); } catch (Throwable t) { } } // We want a NullPointerException here return booleanValue.booleanValue(); } public int getInteger() { Integer integerValue = (value instanceof Number) ? ((Number) value).intValue() : null; if (integerValue == null) { try { integerValue = Integer.valueOf(value.toString()); } catch (Throwable t) { } } return integerValue.intValue(); } public float getFloat() { Float floatValue = (value instanceof Number) ? ((Number) value).floatValue() : null; if (floatValue == null) { try { floatValue = Float.valueOf(value.toString()); } catch (Throwable t) { } } return floatValue.floatValue(); } public static void saveToFile(String path) throws IOException { FileWriter fw = new FileWriter(path); Properties properties = new Properties(); for (Options option : Options.values()) { if (option.saveValue) { properties.setProperty(option.name(), option.getString()); } } if (DEBUG.getBoolean()) { properties.list(System.out); } properties.store(fw, null); } public static void loadFromFile(String path) throws IOException { FileReader fr = new FileReader(path); Properties properties = new Properties(); properties.load(fr); if (DEBUG.getBoolean()) { properties.list(System.out); } Object value = null; for (Options option : Options.values()) { if (option.saveValue) { Class clazz = option.value.getClass(); try { if (String.class.equals(clazz)) { value = properties.getProperty(option.name()); } else { value = clazz.getConstructor(String.class).newInstance(properties.getProperty(option.name())); } } catch (NoSuchMethodException ex) { Debug.log(ex); } catch (InstantiationException ex) { Debug.log(ex); } catch (IllegalAccessException ex) { Debug.log(ex); } catch (IllegalArgumentException ex) { Debug.log(ex); } catch (InvocationTargetException ex) { Debug.log(ex); } if (value != null) { option.setValue(value); } } } } }
这样,我可以轻松地保存和检索文件中的值。 问题是我不想在任何地方重复这个代码。 就像我们所知,枚举不能延长; 所以无论我在哪里使用它,我都必须把所有这些方法放在那里。 我只想声明值,如果它们应该被保留。 每次都没有方法定义; 有任何想法吗?
使用enum
来保存这样的可配置值看起来像是一个完全错误的设计。 枚举是单例,因此有效地在任何给定时间只能激活一个配置。
EnumMap
听起来更像你需要的东西。 它位于enum
的外部,因此您可以根据需要实例化任意数量的配置。
import java.util.*; public class EnumMapExample { static enum Options { DEBUG, ALWAYS_SAVE, THREAD_COUNT; } public static void main(String[] args) { Map normalConfig = new EnumMap(Options.class); normalConfig.put(Options.DEBUG, false); normalConfig.put(Options.THREAD_COUNT, 3); System.out.println(normalConfig); // prints "{DEBUG=false, THREAD_COUNT=3}" Map debugConfig = new EnumMap(Options.class); debugConfig.put(Options.DEBUG, true); debugConfig.put(Options.THREAD_COUNT, 666); System.out.println(debugConfig); // prints "{DEBUG=true, THREAD_COUNT=666}" } }
API链接
-
java.util.EnumMap
用于
enum
类型键的专用Map
实现。 枚举映射中的所有键必须来自创建映射时显式或隐式指定的单个枚举类型。 枚举映射在内部表示为数组。 这种表现非常紧凑和高效。
我尝试用枚举映射和属性文件做类似的事情(请参阅下面的代码)。 但我的枚举很简单,除了嵌入式案例外只有一个值。 我可能有一些更安全的东西。 我会环顾四周。
package p; import java.util.*; import java.io.*; public class GenericAttributes> { public GenericAttributes(final Class keyType) { map = new EnumMap(this.keyType = keyType); } public GenericAttributes(final Class keyType, final Properties properties) { this(keyType); addStringProperties(properties); } public Object get(final T key) { // what does a null value mean? // depends on P's semantics return map.containsKey(key) ? map.get(key) : null; } public boolean contains(final T key) { return map.containsKey(key); } public void change(final T key, final Object value) { remove(key); put(key, value); } public Object put(final T key, final Object value) { if (map.containsKey(key)) throw new RuntimeException("map already contains: " + key); else return map.put(key, value); } public Object remove(final T key) { if (!map.containsKey(key)) throw new RuntimeException("map does not contain: " + key); return map.remove(key); } public String toString() { return toString(defaultEquals, defaultEndOfLine); } // maybe we don;t need this stuff // we have tests for it though // it might be useful public String toString(final String equals, final String endOfLine) { final StringBuffer sb = new StringBuffer(); for (Map.Entry entry : map.entrySet()) sb.append(entry.getKey()).append(equals).append(entry.getValue()).append(endOfLine); return sb.toString(); } public Properties toProperties() { final Properties p = new Properties(); for (Map.Entry entry : map.entrySet()) p.put(entry.getKey().toString(), entry.getValue().toString()); return p; } public void addStringProperties(final Properties properties) { // keep this for strings, but mostly do work in the enum class // ie static GenericAttributes fromProperties(); // which would use a fromString() for (Map.Entry
回答你的评论:
似乎我通过使用枚举作为关键来获取值。 我可能很困惑。
枚举可以实现一个接口,每组枚举可以有一个该基类的实例并委托对它的调用(参见http://java.sun.com/docs/books/effective/toc.html的第 34项)
我发现其他代码与我的通用属性(请参见下文),但我找不到任何测试,我不太确定我在做什么,除了可能添加一些更强大的打字。
我所有这一切的动机是为像picasa这样的照片浏览器存储一些属性,我想在一行属性文件中存储一堆图片的属性
package p; import java.util.*; public enum GA { // like properties, seems like this wants to be constructed with a set of default values i(Integer.class) { Integer fromString(final String s) { return new Integer(s); } Integer fromNull() { return zero; // return empty string? } }, b(Boolean.class) { Boolean fromString(final String s) { return s.startsWith("t")?true:false; } Boolean fromNull() { return false; } }, d(Double.class) { Double fromString(final String s) { return new Double(s); } Double fromNull() { return new Double(zero); } }; GA() { this(String.class); } GA(final Class clazz) { this.clazz=clazz; } abstract Object fromString(String string); abstract Object fromNull(); static GenericAttributes fromProperties(final Properties properties) { final GenericAttributes pas=new GenericAttributes (GA.class); for(Map.Entry
如果您仍在寻找答案,可以尝试使用MIT许可证开源的属性库。 使用它,您不必指定字符串常量,所有内容都将由您定义的枚举确定。 而且,它还有其他一些function。 这个图书馆的亮点是:
- 所有属性键都在一个地方定义,即用户定义的
enum
- 属性值可以包含变量(以
$
sign开头,例如$PATH
),其中PATH
是同一文件中的属性键 - 属性值可以作为指定的数据类型获取,因此无需将字符串值转换为所需的数据类型
- 可以获取属性值作为指定数据类型的列表
- 属性值可以是多行文本
- 可以使属性键成为必需或可选
- 如果值不可用,则可以指定属性键的默认值
- 是线程安全的
你可以在这里找到示例程序