通过复制扩展枚举

时间:2014-01-07 02:56:28

标签: java enums

我希望通过将XResult的值复制到XStatus中来“扩展”枚举XStatus中的枚举XResult。

以下是我的表现方式。如果在XResult中找不到item,则说明为null:

private final String description;
private final Whatever funStuff;

private XStatus(){
  String d=null;
  Whatever f=null;
  try{
    XResult xResult =  XResult.valueOf(this.name());
    d = XResult.toString();
    f = XResult.getWhatever();
  }
  catch (Exception e){
  }

  this.description = d;
  this.funStuff = f;
}

问题是如果XResult不包含这样的项,则会抛出IllegalArgumentException。

问题

  • 这是将一个枚举值复制到另一个枚举的最佳方法吗?问题的原因是,我非常担心使用try-catch来定义枚举。
  • 尝试捕获是否值得花费,如果这确实是唯一的方法。

除了不可靠的手动复制(不跟踪XResult的变化)之外,还有什么其他选择?

其余代码
对于好奇的人来说,这是代码的其余部分,这个问题无关紧要:

private XStatus(final String d){
  this.description = d;
}


public String toString(){
  if (description==null || description.length()==0)
    return doSomethingTo( this.name() );

  return description;
}

public getWhatever(){ /*similar to toString */ }

4 个答案:

答案 0 :(得分:0)

好吧,枚举原则上只是Java对象,因此您可以使用反射来测试XResult是否包含所需的值,甚至是add new values at runtime到您的枚举类。

答案 1 :(得分:0)

仅供参考,如果有人建议将其作为答案,我最初的方法是这样做:

private final String description;
private final Whatever funStuff;

private XStatus(XResult xResult){
  this.description = xResult.toString();
  this.funStuff = xResult.getWhatever();
}

这意味着,而不是声明

enum XStatus {
  //from XResult
  NOT_FOUND,
  DELETED,
  PROCESSED,
  TBD,
  blah, blah ...

}

我必须申报

enum XStatus {
  //from XResult
  NOT_FOUND(XResult.NOT_FOUND),
  DELETED(XResult.DELETED),
  PROCESSED(XResult.PROCESSED),
  TBD(XResult.TBD),
  blah, blah ...
}

答案 2 :(得分:0)

这是如何在没有valueOf的情况下通常使用O(1):

public enum XStatus {
    ;

    private XStatus() {
        XResult xr = Helper.NAMES.get(this.name()); // do whatever with
    }

    static {
        Helper.NAMES = null; // get rid of the map
    }                        // after the constants are instantiated

    private static class Helper {
        static Map<String, XResult> NAMES = new HashMap<String, XResult>();
        static {
            for(XResult xr : XResult.values()) {
                NAMES.put(xr.name(), xr);
            }
        }
    }
}

请注意,这与valueOf基本相同,但您不必尝试使用try-catch。当然valueOf只抛出RuntimeExceptions所以你只需捕获是否有不与XResult并行的常量。

另外,只要把它扔出去,一个更自动的解决方案就是这样:

public final class XStatus {
    private XStatus(XResult xr) {
        //
    }

    private static final XStatus[] TABLE; // or an EnumMap
                                          // with Collections.unmodifiableMap
    static {
        XResult[] xrValues = XResult.values();
        TABLE = new XStatus[xrValues.length];

        for(XResult xr : xrValues) {
            TABLE[xr.ordinal()] = new XStatus(xr);
        }
    }

    public static XStatus xStatusFor(XResult xr) {
        return TABLE[xr.ordinal()];
    }
}

这将反映XResult 1:1,而不依赖于任何类型的String评估。你需要一个XResult来检索它相应的常量,但这就是重点。此外,如果出于某种原因XResult在您的控件之外进行更改,则无需更改XStatus。

答案 3 :(得分:0)

另一种技术可以解决您在将X​​Result类加载到应用程序时动态更改XResult类的问题,即使用字节操作框架(如f.e)在加载时简单地操作其内容。 Javassist

然而,这需要您的应用程序之前没有加载枚举,并且您运行自己的类加载器,它将加载该类加载器中的枚举(以及访问枚举的所有类)(或者在类加载器的子类加载器中)加载了你修改过的枚举字节。)

在使用javassist时,您可以执行类似的操作(尚未使用枚举实现)

public byte[] modifyClass(String className)
{
    try
    {
        // load the bytes from the .class file that actually contains the original definition of the XResult enum
        byte[] xResultBytes = ...

        // define a classpool javassist will use to find the definition for classes
        ClassPool cp = ClassPool.getDefault();
        // add a new search path for class definitions to the class path
        cp = cp.insertClassPath(new ClassClassPath(this.getClass()));
        // add the jar file containing java classes needed to the classpath
        cp = cp.insertClassPath(jarFile.getAbsolutePath());
        // as you do not have loaded XResult with any classloader yet you do not have a Class file of it
        // so you need to provide the loaded bytes and the fully-qualified class name of the enum
        cp = cp.appendClassPath(new ByteArrayClassPath(className, xResultBytes));

        // now you are good to go with modifying the class

        // first create a javassist classrepresentation of the bytes loaded
        CtClass cc = cp.get(className);
        // you can't modify frozen classes
        if (!cc.isFrozen())
        {
            // you only want to instrument the XResult.class
            if (className.endsWith("XResult")) // better replace it with the full name
            {
                // find, add, remove or rename annotations, fields and methods to your liking here
            }
        }

        return cc.toBytecode();
    }
    catch (Exception e)
    {
        // handle error
    }
}

在自定义类加载器中,您可以覆盖findClass以将修改后的字节实际定义为XResult而不是原始字节:

@Override
protected Class<?> findClass(String className) throws ClassNotFoundException
{
    // instead of using the original bytes use the modified byte of the class
    byte[] classBytes = modifyClass(className);
    if (classBytes != null)
    {
        // this will actually create the Class<XResult> enum representation
        return defineClass(className, classBytes, 0, classBytes.length);
    }
    throw new ClassNotFoundException(...);
}

您也可以使用javassist提供的加载程序代替自定义类加载器:

ClassPool pool = ClassPool.getDefault();
 Loader cl = new Loader(pool);

 CtClass ct = pool.get("test.Rectangle");
 ct.setSuperclass(pool.get("test.Point"));

 Class c = cl.loadClass("test.Rectangle");
 Object rect = c.newInstance();

可能你应该反编译枚举类,看看枚举类实际包含什么,以及你想要摆脱什么部分。 F.e如果抛出的异常确实困扰你,你可以简单地删除该方法并将其替换为不抛出异常但只返回null的方法。

看一下javassist的解释性教程: