使用反映/注释修改非空字段的属性

时间:2019-02-13 10:59:06

标签: java reflection

我有这个:

package general;

import org.junit.Test;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;


@Retention(RetentionPolicy.RUNTIME)
@interface SetTable {
  String table();
}

class Star {
  public String foo = "";
  public String toString(){
    return "<Star> : " + this.foo;
  }
}

class Bar {

  @SetTable(table = "xxx")
  public Star s = new Star();
  public String toString(){
    return "<Bar> : " + this.s.toString();
  }
}

class AnnotationInjector {
  public static void inject(Object instance) {
    Field[] fields = instance.getClass().getDeclaredFields();


    for (Field field : fields) {

      if (field.isAnnotationPresent(SetTable.class)) {

        SetTable set = field.getAnnotation(SetTable.class);
        field.setAccessible(true); // should work on private fields

        try {
          field.set(instance, set.table());  // this is not what I need
          //  ***
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }
  }
}

public class AnnotationTest {

  @Test
  public void first() {

    var b = new Bar();
    AnnotationInjector.inject(b);
    System.out.println(b.toString());
  }


}

现在,代码正在尝试将新的Bar()。s设置为字符串,但这将不起作用,因为新的Bar()。s需要是实例Star。但这不是我想做的。我想做的是access并设置它:

new Bar().s.foo = "whatever"

在***指定的上面的行上

我正在做类似的事情:

((Star)field).foo = "whatever";

但这是不对的。分配字段后可以修改字段吗?

2 个答案:

答案 0 :(得分:0)

相反,执行此操作的语法是:

  Star x = (Star)field.get(instance);

此行可以修改Star s上预先存在的Bar字段的属性。

答案 1 :(得分:0)

作为替代方案,我建议您添加一个interface来设置值的方法,以使设置值更通用,而不是检查实例及其类型,并使Star类实现如下:

interface ISetValue {
   void setValue(String value);
}

class Star implements ISetValue{

  public String foo = "";

  @Override
  public void setValue(String value) {
    foo = value; 
  }

  public String toString(){
    return "<Star> : " + this.foo;
  }
}

您的AnnotationInjector类应检查该字段是否实现了ISetValue并为其调用setValue方法,如下所示:

class AnnotationInjector {
  public static void inject(Object instance) {
    Field[] fields = instance.getClass().getDeclaredFields();


    for (Field field : fields) {

      if (field.isAnnotationPresent(SetTable.class)) {

        SetTable set = field.getAnnotation(SetTable.class);
        field.setAccessible(true); // should work on private fields

        try {
          //field.set(instance, set.table());  // this is not what I need
          //  *** 
          //Change is here
           Class fieldType = field.getType();
           if(ISetValue.class.isAssignableFrom(fieldType) ){
                Object fieldValue = field.get(instance);
                Method myMethod = fieldValue.getClass().getInterfaces()[0].getDeclaredMethod("setValue", new Class[]{String.class});
                myMethod.invoke(fieldValue,set.table());
           }
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }
  }
}

现在您的输出应该是:

<Bar> : <Star> : xxx