Java 8,匿名递归嵌套方法

时间:2017-02-19 09:26:48

标签: java recursion lambda functional-programming

我的目标是通过将方法getAllFields重新编写为匿名函数来将其迁移到init中,我知道使用函数式编程很可能。

public void init(){

}

public static Field[] getAllFields(Class klass) {
    List<Field> fields = new ArrayList<Field>();
    fields.addAll(Arrays.asList(klass.getDeclaredFields()));
    if (klass.getSuperclass() != null) {
        fields.addAll(Arrays.asList(getAllFields(klass.getSuperclass())));
    }
    return fields.toArray(new Field[] {});
}

我已经尝试过使用Function和BiFunction,但是他们已经失去了实力。 任何人都可以提供一个如何实现这种情况的片段吗?

2 个答案:

答案 0 :(得分:2)

这还不是直接可行的。在Java 9中,Stream类将具有iterate方法,允许按如下方式实现:

Field[] allFields = Stream
    .iterate((klass, Objects::nonNull, Class::getSuperclass)
    .flatMap(c -> Stream.of(c.getDeclaredFields()))
    .toArray(Field[]::new);

但是,您已经拥有的getAllFields方法是所需功能的一个很好的,干净的实现,并且名称明确地清楚此方法的作用。功能实现将更难理解。

答案 1 :(得分:2)

这个getAllFields实现非常低效,创建了多个ArrayList实例和数组,反复地在它们之间来回复制整个数据。值得庆幸的是,类层次结构很少会成为一个瓶颈。

但是,您可以使用直接循环实现此功能,这样更简单,更高效:

public static Field[] getAllFields(Class<?> klass) {
    List<Field> fields = new ArrayList<>();
    for(; klass!=null; klass=klass.getSuperclass())
        Collections.addAll(fields, klass.getDeclaredFields());
    return fields.toArray(new Field[0]);
}

这里使用递归没有丝毫的好处。

通过循环,您可以轻松创建Function,如果您真的希望:

public void init(){
    Function<Class<?>,Field[]> f = klass -> {
        List<Field> fields = new ArrayList<>();
        for(; klass!=null; klass=klass.getSuperclass())
            Collections.addAll(fields, klass.getDeclaredFields());
        return fields.toArray(new Field[0]);
    };
    Field[] someFields = f.apply(SomeClass.class);
}

但是,当然,甚至没有理由将循环放入Function。由于您希望使用低效的递归实现,您只想在此处使用函数,但lambda表达式根本不支持访问它们自己。他们只能访问实现功能接口的实例所存储的字段如果它存储在您不想要的字段中。使用本地lambda表达式,递归是不可能的。

使用直接循环,您只需编写

即可
public void init(){
    List<Field> fields = new ArrayList<>();
    for(Class<?> klass=SomeClass.class; klass!=null; klass=klass.getSuperclass())
        Collections.addAll(fields, klass.getDeclaredFields());
    Field[] someFields = fields.toArray(new Field[0]);
}
实际上,将fields的内容复制到数组中的确很少有真正的理由,您可以使用List<Field>代替。

也就是说,将循环封装到描述其目的的命名方法中,如getAllFields,实际上是一件好事。如果您不想公开它,请将其声明为private而不是public