Guava - Как применить функцию, возвращающую Void на Iterable

Мне просто интересно, какой лучший способ применить функцию, которая возвращает Void на Iterable/Collection?

У меня есть:

  • У меня есть список объектов Animal
  • Я хочу вызвать у каждого животного списка функцию eat()

У меня есть Function<Animal,Void>, который вызывает input.eat();

Оказывается, когда я звоню:

Collections2.transform(animalList,eatFunction);

Я не считаю это очень элегантным, так как я не ищу преобразования, но только для приложения без вывода. Хуже того, это даже не работает, поскольку преобразования Guava возвращают представления.

Что хорошо работает:

Lists.newArrayList( Collections2.transform(animalList,eatFunction) );

Но это не изящно. Каков наилучший способ применения функции Void к Iterable/Collection, функциональным способом с помощью Guava?

Изменить:

Ответ 1

Как вы думаете, более элегантно? Простой старый цикл for:

for (Animal animal : animalList)
    animal.eat();

или "сгибание процедурного языка путем написания процедурной операции в функциональном стиле" безумие?

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));

Я бы проголосовал за первый случай.

Если вы действительно хотите писать свои программы в функциональном стиле, я бы порекомендовал перейти на другой язык JVM.

Scala может быть хорошей альтернативой для такого случая:

animalList.foreach(animal => animal.eat)

или даже более короткий вариант с использованием заполнителя _:

animalList.foreach(_.eat)

EDIT:

Попробовав код в Eclipse, я обнаружил, что мне нужно добавить оператор return null в eatFunction, потому что 1) Void не совпадает с void и 2) он нереализуем. Что еще страшнее, чем ожидалось! :)

Кроме того, с точки зрения производительности, вызов отложенной функции с использованием некоторого конструктора копирования, подобного описанному выше, также бессмысленно выделяет память. ArrayList того же размера, что и animalList, заполненный только нулями, создается только для немедленного сбора мусора.

Если у вас действительно есть сценарий использования, когда вы хотите передать некоторые функциональные объекты и динамически применить их к некоторым коллекциям, я бы написал свой собственный функциональный интерфейс и метод 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);
        }
    }

}

Затем вы можете аналогичным образом определить функцию void (в нижнем регистре):

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

И используйте это так:

FunctionUtils.forEach(animalList, eatFunction);
// or with a static import:
forEach(animalList, eatFunction);

Ответ 2

Я просто искал то же самое и нашел интерфейс Java Consumer. В вашем случае это будет:

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

Ответ 3

Как отмечали другие, у команды гуавы есть точка зрения, которая исключает это. Если вы ищете другие API, подобные функтору, для того, чтобы делать то, что вы хотите, вы можете воспользоваться функциональной Java Effect или классом джедая Command или Play. ! framework F.Callback или Commons Collections4 Closure [позднее редактировать:] или Java 8+ Consumer -based интерфейсы.