[英]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.