我正在尝试为一组枚举类编写一些Java代码。
每个枚举都封装了一些概念上不同的数据,因此将它们组合起来没有意义。枚举也映射到数据库中的值,因此也共享一些与从数据库加载数据相关的常见操作,包括实例和静态操作。
我需要概括我所拥有的枚举类的集合,这样我就可以将这些枚举中的任何一个传递到另一个类中,该类执行和缓存与每个不同枚举相关的数据库查找。
由于缓存/查找类还将依赖于每个枚举中定义的公共和静态方法,我如何编写解决方案以便保证可以传递给类的任何枚举都具有所需的方法?
通常的方法是定义接口,但接口不允许使用静态方法。
或者,您可以使用抽象类来定义接口和一些常见实现,但我不相信枚举是可行的(我知道枚举必须扩展Enum类并且不能扩展)。< / p>
我有什么选择让我能够确保我的所有枚举都能实现我需要的方法?
枚举示例:
public enum MyEnum{
VALUE_ONE("my data");
VALUE_TWO("some other data");
/**
* Used when mapping enums to database values - if that sounds odd,
* it is: it's legacy stuff
*
* set via private constructor
*/
private String myValue;
//private constructor not shown
public static MyEnum lookupEnumByString(String enumValue){
//find the enum that corresponds to the supplied string
}
public String getValue(){
return myValue;
}
}
答案 0 :(得分:5)
这一切都非常复杂,可能会有错误,但我希望你明白这一点。
// I'm not sure about the right type arguments here
public interface MyEnumInterface<E extends MyEnumInterface & Enum<E>> {
public static boolean aUsefulNonStaticMethod();
String getValue();
MyEnumInfo<E> enumInfo();
}
/** contains some helper methods */
public class MyEnumInfo<E extends MyEnumInterface<E>> {
private static <E extends MyEnumInterface<E>> MyEnumInfo(Class<E> enumClass) {...}
// static factory method
public static <E extends MyEnumInterface<E>> MyEnumInfo<E> infoForClass(Class<E> enumClass) {
... return a cached value
}
public static <E extends MyEnumInterface<E>> MyEnumInfo(E e) {
return infoForClass(e.getClass());
}
// some helper methods replacing static methods of the enum class
E enumForValue(String value) {....}
}
public enum MyEnum implements MyEnumInterface<MyEnum> {
VALUE_ONE("my data");
VALUE_TWO("some other data");
private String myValue; //set via private constructor
//private constructor not shown
public boolean aUsefulNonStaticMethod(){
//do something useful
}
public String getValue(){
return myValue;
}
// the ONLY static method in each class
public static MyEnumInfo<E> staticEnumInfo() {
return MyEnumInfo.infoForClass(MyEnumClass.class);
}
// the non-static version of the above (may be useful or not)
public MyEnumInfo<E> enumInfo() {
return MyEnumInfo.infoForClass(getClass());
}
}
有点奇怪,除了Enum.name()之外你还在使用另一个String,你需要它吗?
由于所有枚举扩展了Enum,你不能让他们共享任何代码。您可以做的最好的事情是将它全部委托给实用程序类中的辅助静态方法。
没有办法强制类实现静态方法,这是可以理解的,因为没有办法(除了反射)来调用它们。
答案 1 :(得分:3)
这是我能想到的最接近的。
您有一个包含常用功能的类:
class Util{
//common functionality
public void doSomething(){
}
}
每个枚举都有一个此类的实例,并且可以在必要时覆盖其方法:
enum Enum1{
FOO,
BAR;
private Util util = new Util();
public Util getUtil() {
return util;
}
}
enum Enum2{
ALICE,
BOB;
private Util util = new Util(){
@Override
public void doSomething() {
//this one has overridden it
};
};
public Util getUtil() {
return util;
}
}
示例用法:
Enum2.ALICE.getUtil().doSomething();
答案 2 :(得分:2)
**警告**以下是Java伪代码,因此不会编译。
因此,您希望将逻辑附加到各个枚举。这可能需要一些枚举来共享相同的逻辑,同时让其他枚举具有自己的特定逻辑。此外,您希望关联可能与Enum名称不同的String键(通常由Enum.name()
返回的内容。
以下基于java的伪代码显示了一种方法(其中之一),您可以在其中执行此操作。它不是唯一的,我并不认为它是最好的。但是,这将是我在这种情况下使用的方法。
也就是说,我会通过接口来进行对象组合(有点像策略+模板模式。)
// at package-level visibility
interface EnumHandler
{
SomeRetVal doSomething(DBEnum dbEnum);
}
final class DefaultHandler implements EnumHandler
{
static DefaultHandler _handler = new DefaultHandler();
SomeRetVal doSomething(DBEnum dbEnum)
{
return ping;
}
}
// at public visibility
public interface Actionable
{
// meh, you might need such an interface, or maybe not, just added here
// for illustration purposes. you'll have to make a decision if you need it
// or not.
SomeRetVal doSomething();
}
// have the enum implement the interface if you determine you need
// such an interface
public Enum DBEnum implements Actionable
{
// ONE and THREE share the same logic. TWO has its own.
ONE("db-key-one" ),
TWO("db-key-two, new EnumHandler(){
SomeRetVal doSomething(DBEnum dbEnum){ return pong; } } ),
THREE("db-key-three");
// this guy keeps track of enums by key
static private java.util.Map<String,DBEnum> _MAP =
java.util.Collections.unmodifiableMap(
new java.util.HashMap<String,DBEnum>() );
final private String _key;
final private EnumHandler _handler;
// allows construction of customized handler
DBEnum(final String key, final EnumHandler handler)
{
this._key = key;
this._handler = handler;
this._MAP.put(key, this)
}
// construct using default handler
DBEnum(final String key)
{
this(key, DefaultHandler._handler);
}
// have toString() return the key instead of this.name()
public String toString()
{
return this._key;
}
// implementing Actionable interface (if you choose to use such an interface)
public SomeRetVal doSomething()
{
return this._handler.doSomething(this);
}
// get enum by key
public static DBEnum getByDbKey(final String key)
{
DBEnum retVal = this._MAP.get(key);
if( retVal == null ){ throw new IllegalArgumentException("thingie not found");
return retVal;
}
public static Iterator<String> dbKeys()
{
return _map.keySet().iterator();
}
}
// somewhere else
public static void main(String[] args)
{
DBEnum.ONE.doSomething();
DBEnum.geByDBKey( DBEnum.TWO.toString() ).doSomething();
for( String dbKey : DBEnum.dbKeys() )
{
DBEnum.getByDbKey( dbKey ).doSomething();
}
// the following will kaput with an IllegalArgumentException
DBEnum.getDbByKey( "key-that-is-not-there" ).doSomething();
}
理论上,当类加载器加载Enum时,可以从资源文件中提取实际的数据库键。资源文件的内容(以及对内容的更改)可以是部署项。这可能有很大的优势 - 更改db键不需要重新编译。 但是这样的方法会让事情变得复杂一些。这将是我在其他一切正确完成之后实施的。