用反射连续迭代场

时间:2018-12-09 15:47:23

标签: java android reflection android-reflection

请避免仅在Kotlin中提供答案,且答案要高于Android 21。

我正在尝试构建一个API解析器,该解析器使用类层次结构逻辑来表示API层次结构本身。通过这种结构,我能够以一种不复杂的方式解析该API,并且已经能够实现,但是我想进一步改进它。

我将开始解释我已经实现的内容。

这是我的应用将通过GET接收,内部解析和分发的示例URL:

http://www.example.com/news/article/1105

在应用程序中,基本域无关紧要,但随后是API结构。

在这种情况下,我们混合使用了命令和变量:

  • 新闻(命令)
  • 文章(命令)
  • 1105(可变)

要建立什么是命令和什么是变量,我构建了以下类结构:

public class API {
    public static final News extends AbstractNews {}
}

public class AbstractNews {
    public static final Article extends AbstractArticle {}
}

public class Article {
    public static void GET(String articleId) {
        // ...
    }
}

然后我在拆分URL之后对每个类进行迭代,同时将每个命令与从API类开始的每个类(或子类)进行匹配。在我到达拆分URL的末尾之前,所有失败的匹配项都作为变量存储在单独的列表中。

上面提供的示例的过程如下:

每个反斜杠的分隔URL(忽略基本域)

/news/article/1105

List<String> stringList = [
    news, 
    article,
    1105
];

迭代拆分列表中的每个项目,然后再次匹配API结构化的类(以下仅是示例示例,它不是我当前实现的100%):

List<String> variableList = new ArrayList<>();
Class lastClass = API.class;

for (String stringItem : stringList) {

    if ((lastClass = classHasSubClass(lastClass, stringItem)) != null) {
        continue;
    }

    variableList.add(stringItem);

}

到达列表末尾后,我检查最后一个类是否包含请求方法(在这种情况下为GET),并与变量列表一起调用。

就像我之前所说的那样,它可以正常工作,但是它使每个类都直接暴露出来,因此,任何其他从事该项目的人都可以直接,不正确地访问它们,因此,我试图使层次结构更具约束力。

我也希望保持通过层次结构访问方法的能力,因此仍然可以实现以下目的:

API.News.Article.GET(42334);

同时我不希望也可以执行以下操作:

AbstractArticle.GET(42334);

我尝试将每个子类改成类实例字段

public class API {
    // this one is static on purpose to avoid having to instantiate
    // the API class before accessing its fields
    public static final AbstractNews News = new AbstractNews();
}

public class AbstractNews {
    public final AbstractArticle Article = new AbstractArticle();
}

public class Article {
    public void GET(String articleId) {
        // ...
    }
}

这对于我之前想要达到的两点效果很好,但是我无法找到一种方法来迭代类字段,从而无法正确调用最终方法。

对于以前的逻辑,我需要迭代的内容如下:

private static Class classHasSubClass(Class<?> currentClass, String fieldName) {

    Class[] classes;

    classes = currentClass.getClasses();

    for (final Class classItem : classes) {
        if (classItem.getSimpleName().toLowerCase().equals(fieldName)) {
            return classItem;
        }
    }

    return null;

}

但是对于第二次尝试使用字段的逻辑,我无法正确调用final方法,这可能是因为最终的逻辑实际上是在尝试执行以下操作:

AbstractArticle.GET(42334);

代替

API.News.Article.GET(42334);

我怀疑是因为invoke方法的第一个参数不再像以前一样是null,而必须正确地与API.News.Article.GET(42334);

等效。

有没有办法使这项工作有效,或者有更好/不同的方式做到这一点?

1 个答案:

答案 0 :(得分:0)

我发现我在使用实例字段的正确路径上,但是缺少必要的信息的一部分,以便最后正确地调用该方法。

当迭代字段时,我只使用每个字段的类,在使用静态类引用之前,这是完美的,因为它们不是实例,但是现在它需要该字段的实例才能正常工作。

最后,代替classHasSubClass使用的迭代方法如下:

private static Object getFieldClass(Class<?> currentClass, Object currentObject, final String fieldName) {

    Field[] fieldList;

    fieldList = currentClass.getDeclaredFields();

    for (final Field field : fieldList) {
        if (field.getName().toLowerCase().equals(fieldName)) {

            try {
                return field.get(currentObject);
            } catch (IllegalAccessException e) {

                e.printStackTrace();
                break;

            }

        }
    }

    return null;

}

为此,我总是保留一个实例对象引用,该引用要作为第一个参数(someMethod.invoke(objectInstance);而不是null传递给我要调用的最终字段。