繁体   English   中英

通过复制扩展枚举

[英]Extending an Enum by copying

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

这就是我的做法。 如果在XResult中未找到item,则说明保留为空:

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。

问题

  • 这是将一个枚举值复制到另一个枚举的最佳方法吗? 问题的原因是,我为尝试使用枚举来定义枚举感到非常困扰。
  • 如果确实是唯一的方法,那么尝试捕获值得付出的代价是否值得?

除了不可靠的手动复制(不跟踪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 */ }

好吧,枚举原则上只是Java对象,因此您可以使用反射来测试XResult是否包含所需的值,甚至在运行时将新值添加到枚举类。

仅供参考,以防万一有人建议将此作为答案,我的最初方法是这样做:

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 ...
}

这是通常在O(1)中没有valueOf的情况下执行的方法:

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相同,但是您不必尝试捕获。 当然,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。

另一种可能解决您的问题的方法是,在将XResult类加载到应用程序时动态地更改它,该方法是在加载时使用诸如fe 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();

可能您应该反编译一个枚举类,并查看枚举类实际包含什么以及想要摆脱的部分。 例如,如果引发的异常困扰您,您可以简单地删除该方法,并将其替换为不引发异常但仅返回null的方法。

看看javassist的非常讲解的教程:

暂无
暂无

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

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