web-dev-qa-db-ja.com

Guava-IterableでVoidを返す関数を適用する方法

_Iterable/Collection_にVoidを返す関数を適用する最良の方法は何でしょうか?

私のユースケースは次のとおりです。

  • Animalオブジェクトのリストがあります
  • リストの各動物にeat()関数を呼び出したい

input.eat();を呼び出す_Function<Animal,Void>_があります

私が電話すると、次のことがわかります。

_Collections2.transform(animalList,eatFunction);
_

私は変換を探しているのではなく、出力のないアプリケーションのみを探しているので、これは非常にエレガントだとは思いません。最悪の場合、グアバ変換がビューを返しているため、機能しません。

正常に機能するのは次のとおりです。

_Lists.newArrayList( Collections2.transform(animalList,eatFunction) );
_

しかし、それはエレガントではありません。 Guavaを使用して機能的に、Void関数を反復可能/コレクションに適用する最良の方法は何ですか?

編集

13

何がよりエレガントだと思いますか?昔ながらの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)

編集:

Eclipseでコードを試した後、1)eatFunctionVoidと同じではなく、2)インスタンス化できないため、return nullステートメントをvoidに追加する必要があることがわかりました。それは予想よりもさらに醜いです! :)

また、パフォーマンスの観点から、上記のようなコピーコンストラクターを使用してレイジー関数を呼び出すと、メモリが無意味に割り当てられます。 nullのみで埋められた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);
27
Natix

私はちょうど同じものを探していて、Javaコンシューマーインターフェイスを見つけました。あなたの場合は次のようになります:

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

他の人が指摘したように、グアバチームはこれを行うことを妨げる視点を持っています。やりたいことを実行するための他のファンクターのようなAPIを探している場合は、 関数型JavaのEffect 、または ジェダイのCommandクラス 、または Play!フレームワークのF.Callback 、または CommonsCollections4のClosure [後で編集:]または Java 8+ Consumer ベースのインターフェース。

5
Kevin Welker