当字段存在时出现NoSuchFieldException异常

90

当我尝试运行以下方法时,出现了java.lang.NoSuchFieldException的错误:

 public void getTimes(String specialty, String day) {
    ArrayList<Tutor> withSpec = new ArrayList<Tutor>();
    for (Tutor t : tutorList){
        try {
            Time startTime = (Time)t.getClass().getField(day + "Start").get(t);
        } catch (NoSuchFieldException | SecurityException | IllegalAccessException ex) Logger.getLogger(DBHandler.class.getName()).log(Level.SEVERE, null, ex); }

错误出现在这一行:Time startTime = (Time)t.getClass().getField(day + "Start").get(t); 我不理解这个错误,因为monStart是Tutor类的一个字段:
Public class Tutor implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@NotNull
@Column(name = "tutorID")
private Integer tutorID;

.... 

@Column(name = "monStart")
@Temporal(TemporalType.TIME)
 Date monStart;

我刚开始学习使用反射技术,所以我确定这是一种语法错误...


它导致你的应用程序崩溃了吗? - IgorGanapolsky
7个回答

175
getField方法只能找到public修饰的字段。如果要找到任何在类上直接声明的字段,即使不是public修饰的,你需要使用getDeclaredField方法。

1
即使在使用getDeclaredField而不是getField之后,我仍然会遇到此错误:无法访问具有修饰符“private”的类的成员。 - Sudarshan
10
明白了,我需要使用 setAccessible(true)。 - Sudarshan
5
即使使用 getDeclaredField() 方法,仍然出现了 NoSuchFieldException 异常,即使使用 setAccessible(true) 方法后,仍然出现了 "has private access" 错误。示例1:执行 Field fieldy = rootElement.getClass().getDeclaredField("name"); 时抛出 NoSuchFieldException 异常。但是,执行 Field[] fields = rootElement.getClass().getDeclaredFields(); 可以让我遍历字段,在调用 field.getName() 时返回值为 "name"。所以这是怎么回事? - Joe Flack
2
解决了:看起来我必须用 try/catch 包装一下。 - Joe Flack
2
@JoeFlack,使用try / catch包围解决了什么问题?异常仍然会被抛出,那么您在catch中该如何正确获取存在但某种方式引发NoSuchFieldException的字段? - payne
1
如果该字段是从超类继承而来的,那么这个答案就不够了。请参见 https://dev59.com/YGUp5IYBdhLWcg3wPFpK#69946642。 - payne

13
根据javadoc,Class.getField()返回一个Field对象,该对象反映了由此Class对象表示的类或接口的指定公共成员字段。 如果要访问非公共字段,请使用getDeclaredField()

9

如果您是Android开发人员,并且仍然无法解决问题,请检查是否启用了Proguard。 如果是,则可能正在混淆相关类,您需要添加规则以防止这种情况发生。


非常感谢。我的类也被混淆了。我必须禁用minifyEnabled标志以防止它。 - theimpulson

8

解决getClass().getField()问题的最佳方法是:

  1. 使用getDeclaredField()代替getField():
        String propertyName = "test";
        Class.forName(this.getClass().getName()).getDeclaredField(propertyName);

请将"HelloWorld"替换为你的类名:
        String propertyName = "name";
        HelloWorld.class.getDeclaredField(propertyName);

如果您想获取列的注释长度:

HelloWorld.class.getDeclaredField(propertyName).getAnnotation(Column.class).length();

5

如已在接受的答案中提到,使用 getDeclaredField 可能会解决你的问题(如果该字段未声明为 public)。

如果你仍然收到 NoSuchFieldException,那么可能是因为该字段实际上在超类中!确实,如果你的类 extends 另一个类,则无法通过 getDeclaredField 方法获取继承的字段。以下是如何解决该问题:

String propertyName = "foo";
yourClass.getClass().getSuperClass().getDeclaredField(propertyName);

问题已解决,谢谢! - KCD
如何为继承的字段设置值?当我尝试使用“field.set(...)”时,会出现“NoSuchFieldException”的错误。 - Amirhosein Al

2

我看到这个问题的标题后来到了这里。在我的Android项目中,我也遇到了相同的错误(NoSuchFieldException),但原因不同。

所以对于其他人来说,这个错误也有可能是由于Android Studio中缓存失去同步导致的。请前往文件 > 无效缓存 / 重新启动...

参见这里也可以了解更多信息。


0

好知道...如果你的类有@Configuration注解,getDeclaredFields()将不包含你的字段。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接