- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我制作的程序可以使用反射生成类、枚举和接口(interface)的源代码,但我在生成枚举时遇到问题。
我的 EumTest 类(class)
public enum EnumTest{
a,b;
private String r;
private EnumTest(){
}
private void some(){
}
public int[] some2(int[] b){
return b;
}
}
生成枚举文件的方法
private void generateEnum(Class<?> cls,PrintWriter writer) {
this.writePackage(cls, writer);
this.writeAnnotations(writer, cls.getDeclaredAnnotations());
writer.write(Modifier.toString(cls.getModifiers())+ " enum " + cls.getSimpleName());
this.writeImplementation(cls, writer);
writer.write("{");
this.writeNewLine(writer);
Object[] cons = cls.getEnumConstants();
for (int i = 0; i < cons.length; i++) {
writer.write(cons[i].toString());
if(i != cons.length - 1)
writer.write(",");
}
writer.write(";");
this.writeNewLine(writer);
this.writeFields(cls, writer);
this.writeConstructors(cls, writer);
this.writeMethods(cls,writer);
writer.write("}");
}
结果是这个新的枚举
package metaprogramovanie.test;
public final enum EnumTest{
a,b;
private java.lang.String r;
private static final metaprogramovanie.test.EnumTest[] $VALUES;
private EnumTest(java.lang.String arg0,int arg1){
}
public static metaprogramovanie.test.EnumTest[] values(){
return null;
}
public static metaprogramovanie.test.EnumTest valueOf(java.lang.String arg0){
return null;
}
private void some(){
}
public int daco(int arg0){
return 0;
}
}
正如您所看到的,存在一些错误。例如修饰符生成FINAL并且枚举不能是最终的,接下来有更多的方法和字段,构造函数有参数......
构造函数生成
private void writeConstructors(Class<?> cls, PrintWriter writer){
Constructor[] cons = cls.getDeclaredConstructors();
for (Constructor constructor : cons) {
this.writeAnnotations(writer, constructor.getDeclaredAnnotations());
writer.write(Modifier.toString(constructor.getModifiers()) + " " + cls.getSimpleName());
this.writeParameters(writer,constructor.getParameters());
writer.write("{");
this.writeNewLine(writer);
writer.write("}");
this.writeNewLine(writer);
}
}
字段生成
private void writeFields(Class<?> cls, PrintWriter writer){
Field[] atr = cls.getDeclaredFields();
for (Field field : atr) {
if(field.isEnumConstant()){
System.out.println("JE");
}
else{
this.writeAnnotations(writer, field.getDeclaredAnnotations());
writer.write(Modifier.toString(field.getModifiers())+" " + field.getType().getTypeName()+ " " + field.getName());
if(Modifier.isStatic(field.getModifiers())){
try{
Object value = field.get(null);
writer.write(" = " + this.getFieldValue(field));
}catch(IllegalAccessException ex){
}
}
writer.write(";");
this.writeNewLine(writer);
}
}
this.writeNewLine(writer);
}
方法生成
private void writeMethods(Class<?> cls, PrintWriter writer){
Method[] methods = cls.getDeclaredMethods();
for (Method met : methods) {
this.writeAnnotations(writer, met.getDeclaredAnnotations());
writer.write(Modifier.toString(met.getModifiers())+" "+met.getReturnType().getTypeName() +" "+ met.getName());
this.writeParameters(writer, met.getParameters());
writer.write("{");
this.writeNewLine(writer);
if(!met.getReturnType().equals(Void.TYPE)){
this.writeMethodBody(writer,met);
}
this.writeNewLine(writer);
writer.write("}");
this.writeNewLine(writer);
}
this.writeNewLine(writer);
}
如果您有任何想法如何获取可编译的枚举。
最佳答案
这里解释了为什么你会得到所有额外的东西。
枚举是使用编译器合成的大量代码来实现的,因此它们在“幕后”按照 Java 语言规范指定的方式运行,而不需要对 JVM 进行大幅更改。
Java Language Specification例如,说:
enum
隐式是 final
,除非存在带主体的常量。enum
都会隐式声明 values()
和 valueOf(String)
方法。因此,您获得了 EnumTest
的 final
修饰符,并且获得了两个额外的方法。
此外,编译器还会综合一些代码来帮助高效地实现一些所需的内容。例如,values()
方法要复制和返回的数组的源被放置在名为 $VALUES
的合成字段中。它在枚举初始化时被填充,然后每当调用 values()
时都可以复制它。
此外,为了正确初始化常量,编译器向每个构造函数添加了两个隐藏参数。因此,如果您有一个空的构造函数,那么它在幕后实际上有两个参数 - 常量的名称和序号。如果您有一个只有一个参数的构造函数,那么在幕后它将有 3 个参数。
当常量拥有自己的主体时,会创建额外的合成代码。
编译器完成的所有这些后面的实现都没有记录在 JLS 中,并且基本上由任何编译器来决定如何实现。只要枚举作为编译器生成的任何代码的结果而表现正确,就可以自由地以它选择的任何方式生成它。
<小时/>克服您遇到的一些问题的提示:
不要使用 Modifier.toString(cls.getModifiers())
来打印 enum
修饰符,而是使用
Modifier.toString(cls.getModifiers() & ~ Modifier.FINAL & ~ Modifier.ABSTRACT)
这会从输出中排除 final
修饰符以及 abstract
修饰符,如果您向 enum
添加抽象方法,则会显示该修饰符(并处理常数体,这是您到目前为止尚未完成的事情)。
Field.isSynthetic()
方法检查某个字段是否由编译器合成,并且不输出该方法返回 true 的字段。这将删除 $VALUES
字段。values()
和 valueOf(String)
这两个方法不被视为合成方法,如果您对它们使用 Method.isSynthetic()
,它将返回 false。因此,您只需检查在枚举中找到的每个方法即可。如果是这两个方法之一,且签名和返回类型与 JLS 中指定的相同,则跳过它们并且不输出它们。棘手的部分是去掉每个构造函数的两个附加参数。基本上,当您迭代构造函数参数时,可以跳过前两个参数。但是,请记住,这仅适用于 Oracle 编译器生成的代码,并且不能保证在未来版本或任何其他编译器中!
关于Java反射-枚举生成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30832118/
我是一名优秀的程序员,十分优秀!