Guava - 如何应用在Iterable上返回Void的函数

Seb*_*ber 13 java functional-programming guava

我只是想知道应用返回函数的最佳方法VoidIterable/Collection什么?

我的用例是:

  • 我有一个Animal对象列表
  • 我想呼吁列表中的每个动物的eat()功能

我有一个Function<Animal,Void>电话input.eat();

事实证明,当我打电话时:

Collections2.transform(animalList,eatFunction);
Run Code Online (Sandbox Code Playgroud)

我不觉得这很优雅,因为我不是在寻找转换,而只是为了没有任何输出的应用程序.最糟糕的是,由于Guava转换正在返回视图,因此它甚至无法工作.

什么工作正常是:

Lists.newArrayList( Collections2.transform(animalList,eatFunction) );
Run Code Online (Sandbox Code Playgroud)

但它并不优雅.使用Guava以函数方式将Void函数应用于Iterable/Collection的最佳方法是什么?

编辑:

  • 相关问题:Guava供应商<T>的逆转
  • 同样正如Dzik指出的那样,如果您使用的是Java8而不是Guava,那么就是Consumer类:https://stackoverflow.com/a/32222080/82609

Nat*_*tix 27

你觉得什么更优雅?一个普通的旧循环:

for (Animal animal : animalList)
    animal.eat();
Run Code Online (Sandbox Code Playgroud)

或者"通过编写功能风格的程序操作来弯曲程序性语言"的疯狂?

static final Function<Animal, Void> eatFunction = new Function<Animal, Void>() {
    @Override
    public Void apply(Animal animal) {
        animal.eat();
        return null; // ugly as hell, but necessary to compile
    }
}

Lists.newArrayList(Collections2.transform(animalList, eatFunction));
Run Code Online (Sandbox Code Playgroud)

我会投票给第一个案子.

如果您真的想以函数式编写程序,我建议您切换到另一种JVM语言,而不是对名为Java的老太太强奸.:)

对于这种情况,Scala可能是一个很好的选择:

animalList.foreach(animal => animal.eat)
Run Code Online (Sandbox Code Playgroud)

甚至是使用_占位符的较短变体:

animalList.foreach(_.eat)
Run Code Online (Sandbox Code Playgroud)

编辑:

在Eclipse中尝试代码之后,我发现我必须将return null语句添加到eatFunction,因为1)Void不同于void和2)它是不可实现的.那比预期的更难看!:)

同样从性能的角度来看,通过使用上面的一些复制构造函数来调用惰性函数也毫无意义地分配内存.一个ArrayList为大小相同的animalList用null不仅填补创建只是要立即垃圾收集.

如果你真的有一个用例要传递一些函数对象并在一些集合上动态应用它们,我会编写自己的函数接口和foreach方法:

public interface Block<T> {
    void apply(T input);
}

public class FunctionUtils {
    public static <T> void forEach(Iterable<? extends T> iterable,
            Block<? super T> block) {
        for (T element : iterable) {
            block.apply(element);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

然后你可以类似地定义一个void(小写)函数:

static final Block<Animal> eatFunction = new Block<Animal>() {
    @Override
    public void apply(Animal animal) {
        animal.eat();
    }
};
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

FunctionUtils.forEach(animalList, eatFunction);
// or with a static import:
forEach(animalList, eatFunction);
Run Code Online (Sandbox Code Playgroud)

  • 当然,类似函数的类的被黑客声明是丑陋的,但是在需要应用参数化动作或一系列动作的情况下,如果没有Visitor模式和类似仿函数的对象的实现,怎么做呢?所以实际上有合理的用例.在功能API中使用详细的真实函数声明也是相同的论据,即使是由Guava团队自己,但是他们实现了函数,因为他们意识到有用例需要它,只要首先仔细权衡这些缺点. (4认同)

Dzi*_*zik 7

我只是在寻找相同的东西,并找到了一个Java Consumer界面.在你的情况下,它将是:

final Consumer<Animal> action = new Consumer<Animal>() {
    @Override
    public void accept(Animal animal) {
        animal.eat();
    }
};    
...
FluentIterable.from(animals).forEach(action);
Run Code Online (Sandbox Code Playgroud)

  • 很高兴知道.如果是番石榴本来会接受的 (2认同)

Kev*_*ker 5

正如其他人所指出的那样,番石榴团队的观点不允许这样做.如果您正在寻找其他类似仿函数的API来做您想做的事情,您可以查看Functional JavaEffect,或者Jedi的Command课程,或者Play!框架F.Callback,或Commons Collections4的Closure [后编辑:]或基于Java 8+Consumer的接口.