web-dev-qa-db-ja.com

クラスでJavaストリームをフィルタリングする場合、instanceofの代わりはありますか?

1つのクラスを拡張するすべての型がJavaコレクションにパックされているプロジェクトで予期しない状況が発生しましたが、そのクラスの特定の拡張のみに追加のメソッドが含まれています。それを " ) ";そして、他の拡張機能がそれを持たないことを明確にしましょう。そのコレクション内のすべてのアイテムに対して1つのタスクを実行する直前に、それを実装するすべてのアイテムに対してalso()を呼び出す必要があります。

最も簡単な方法はこれです:

stuff.stream().filter(item -> item instanceof SpecificItem)
            .forEach(item -> ((SpecificItem)item).also()));
stuff.stream().forEach(item -> item.method());

正常に動作しますが、そこにある「instanceof」に慣れていません。これは一般的にコードの悪臭のマーカーです。このクラスを取り除くためだけに、このクラスをリファクタリングする可能性は非常に高いです。しかし、劇的なことをする前に、コミュニティに問い合わせて、StreamsまたはCollectionsのどちらかについてより経験のある人がより簡単な解決策を持っているかどうかを確認したいと思いました。

例(確かに非排他的)として、クラスによってエントリをフィルタリングするコレクションのビューを取得することは可能ですか?

私は.map呼び出してキャストを行います。その後、後のコードで「本物」を使用できます。

前:

stuff.stream().filter(item -> item instanceof SpecificItem)
            .forEach(item -> ((SpecificItem)item).also()));

後:

stuff.stream().filter(item -> item instanceof SpecificItem)
            .map(item -> (SpecificItem)item)
            .forEach(specificItem -> specificItem.also()));

これは完璧ではありませんが、少し片付けているようです。

3
Jon Onstott

1つのクラスを拡張するすべての型がJavaコレクションにパックされているプロジェクトで予期しない状況が発生しましたが、そのクラスの拡張のみがメソッドを実装しています。それを "also()"と呼びましょう。そのコレクション内のすべてのアイテムに対して1つのタスクを実行する直前に、それを実装するすべてのアイテムに対してalso()を呼び出す必要があります。

それは明らかに欠陥のある設計です。あなたが書いたものからは明らかではありません、それが何を意味するのか、あるクラスはimplementメソッドではないということです。それがnothingを単に行う場合、それは問題ではないので、不要な副作用があると思います。

正常に動作しますが、そこにある「instanceof」に慣れていません。

あなたの根性は正しいです。オブジェクト指向の優れたデザインは、オブジェクトが正確に何であるかを知ることなく機能し、特別な種類のインスタンスであるかどうかに関係します。

しかし、劇的なことをする前に、コミュニティに問い合わせて、StreamsまたはCollectionsのどちらかについてより多くの経験がある人がより簡単な解決策を持っているかどうかを確認するつもりだと思いました。

リファクタリングは劇的ではありません。コードの品質が向上します。

与えられたコードベースで最も簡単な解決策は、確実にするために、親と子クラスを持つ2つの別々のコレクションを用意することです。しかし、それはかなりきれいではありません。

3
Thomas Junk

SpecificItemalso()が実際に何であるかを知らずに特定の推奨事項を提供することは困難ですが、

SpecificItemのスーパークラスでalso()を定義します。何もしないデフォルトの実装を指定します(空のメソッド本体を指定してください)。必要に応じて、個々のサブクラスでオーバーライドできます。 alsoが実際に何であるかによって、関係するすべてのクラスにとって意味のある名前に変更する必要がある場合があります。

2
Alex D

ここにも明らかなものが欠けていないことを願っています(これも @ Tristan Burnsideのコメント で示唆されています)が、なぜSpecificItem.method()が最初にalso()を呼び出せないのですか?

public class SpecificItem extends Item {
    ...
    public void method() {
        also();
        super.method();
    }
}

例(確かに非排他的)として、クラスによってエントリをフィルタリングするコレクションのビューを取得することは可能ですか?

おそらくパフォーマンスへの影響(YMMV)を犠牲にして、クラスのキーとしてcollect()Collectors.groupingBy()にして、結果として得られたMapから必要なものを選択するという、ストリームyの方法を考えることができます。値はListとして格納されるため、Setに対してこのようなフィルタリングを行うことを期待していて、フィルタリングされたSetを取得したい場合は、結果のSetにも値を追加するための追加の手順が必要になります。

1
h.j.k.

代替:

  1. make SpecificItemインターフェイスを実装します(「フィルター可能」と言います)
  2. streamを拡張する新しいクラスを作成する
  3. インターフェイスを実装するオブジェクトを受け入れる新しい 'filter'メソッドを作成します
  4. 元のストリームメソッドをオーバーライドし、実装にリダイレクトします(インターフェイスにpredicateパラメータをキャストすることにより)

そうすれば、インターフェイスを実装するオブジェクトだけがテーマ自体をメソッドに渡すことができます... instanceofを使用する必要はありません

public class MyStream extends Stream
{
    //...

    Stream<Filterable> filter(Predicate<? implements Filterable> predicate)
    {
         return super.filter(predicate);
    }
}
1
ymz