简体   繁体   English

通过复制扩展枚举

[英]Extending an Enum by copying

I wish to "extend" enum XResult in enum XStatus, by copying the values of XResult into XStatus. 我希望通过将XResult的值复制到XStatus中来“扩展”枚举XStatus中的XResult。

Here is how I'm doing it. 这就是我的做法。 If item is not found in XResult, description is left null: 如果在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;
}

The issue is that if XResult does not contain such an item, it would throw IllegalArgumentException. 问题是,如果XResult不包含此类项目,它将抛出IllegalArgumentException。

Question : 问题

  • Is this the best way to copy values of one enum into another? 这是将一个枚举值复制到另一个枚举的最佳方法吗? The reason for the question is, I am deeply troubled with having the expense of try-catch to define the enum. 问题的原因是,我为尝试使用枚举来定义枚举感到非常困扰。
  • Is the try-catch worthwhile for its expense, if indeed this is the only way. 如果确实是唯一的方法,那么尝试捕获值得付出的代价是否值得?

What are my alternatives, besides unreliable manual copying (which does not track changes in XResult)? 除了不可靠的手动复制(不跟踪XResult的更改)之外,我还有什么选择?

Rest of the code : 其余代码
For the curious, this is the rest of the code that is inconsequential to the question: 出于好奇,这是与问题无关紧要的其余代码:

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是否包含所需的值,甚至在运行时将新值添加到枚举类。

Just for reference, and in case someone suggests this as answer, my initial way was to do it this way: 仅供参考,以防万一有人建议将此作为答案,我的最初方法是这样做:

private final String description;
private final Whatever funStuff;

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

Which means, instead of declaring 这意味着,而不是声明

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

}

I would have to declare 我必须声明

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

This is how to do it without valueOf in generally O(1): 这是通常在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);
            }
        }
    }
}

Note that this is basically the same as valueOf but you don't have to try-catch. 请注意,这基本上与valueOf相同,但是您不必尝试捕获。 Of course valueOf only throws RuntimeExceptions so you only have to catch if you have constants that do not parallel XResult. 当然,valueOf只抛出RuntimeExceptions,因此,只有在常量与XResult不平行的情况下,才需要捕获。

Also, just throwing this out there, a more automatic solution would be something like this: 另外,将其扔在那里,更自动化的解决方案将是这样的:

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()];
    }
}

That will mirror XResult 1:1 without relying on any kind of String evaluation. 这将镜像XResult 1:1,而无需依赖任何类型的String评估。 You need an XResult to retrieve its corresponding constant but that's the point. 您需要一个XResult来检索其相应的常量,但这就是重点。 Also if for some reason XResult were to change outside of your control you don't need to change XStatus. 同样,如果由于某种原因XResult在您的控制范围之外进行更改,则无需更改XStatus。

A further technique which might solve your problem to dynamically change the XResult class on loading it to your application is to simply manipulate its content at load-time using a byte manipulation framework like fe Javassist . 另一种可能解决您的问题的方法是,在将XResult类加载到应用程序时动态地更改它,该方法是在加载时使用诸如fe Javassist之类的字节操作框架简单地操作其内容。

This requires however that your application hasn't loaded the enum before and you run your own classloader which will load the enum (and all the classes that access the enum) in that classloader (or in a child classloader of the classloader that loaded the bytes of the enum you have modified). 但是,这需要您的应用程序之前没有加载过枚举,并且您运行自己的类加载器,它将在该类加载器(或加载了字节的类加载器的子类加载器)中加载该枚举(以及所有访问该枚举的类)已修改的枚举数)。

On using javassist you could do something like that (haven't done it actually with enums yet) 在使用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
    }
}

In a custom classloader you can override findClass to actually define the modified bytes as XResult instead of the original bytes: 在自定义类加载器中,您可以覆盖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(...);
}

Instead of a custom classloader you could also use the loader javassist provides: 除了自定义类加载器,您还可以使用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();

Probably you should decompile an enum class and see what an enum class actually contains and what part is you want to get rid of. 可能您应该反编译一个枚举类,并查看枚举类实际包含什么以及想要摆脱的部分。 Fe if the thrown exception does bother you you could simply remove the method and replace it with one that don't throws the exception but returns simply null. 例如,如果引发的异常困扰您,您可以简单地删除该方法,并将其替换为不引发异常但仅返回null的方法。

Have a look at the very explanatory tutorials javassist have: 看看javassist的非常讲解的教程:

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

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