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


8

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

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

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

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

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


2
でフィルタリングする必要がありSpecificItem、これが述語を指定する適切な方法である場合、なぜinstanceofそこにいるのかについてきしむのはなぜですか?
Robert Harvey

ストリーム内のオブジェクトは、クラスを区別できるようにするいくつかの一般的なインターフェイスを実装していますか?一部.getKind()または.isSpecific()
9000年

@ 9000残念ながらありません。そのようなものを追加すると、もう少し混乱するでしょう。
Michael Eric Oberlin

8
SpecificItem実装がalso()最初に呼び出されない理由はありますか?
Tristan Burnside

1
@LuísGuilhermeにIterablestream()メソッドはありませんが、でforEach()直接呼び出すことができますIterable
shmosel

回答:


3

私は.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()));

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


私ができる限り多くのキャストを作業コードから削除することが私の興味です。そうです、まさにそのためのマップです。
Michael Eric Oberlin、

より機能的に: stuff.stream().filter(item -> item instanceof SpecificItem).map(SpecificItem.class::cast).forEach(SpecificItem::also);
strickli

3

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

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

それは正常に動作しますが、「instanceof」がそこにあるのは快適ではありません。

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

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

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

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


質問をより具体的になるように編集しました。さらに明確にするために、これはすぐに必要な変更のためのクラックジョブです。クラスの拡張を考慮する必要があります。リファクタリングに関しては、それがプログラマーであることの本質的な部分であるとだけ言いましょう。しかし、私は可能な限り劇的なリファクタリングを探しています。
Michael Eric Oberlin

あなたの質問には、明確な答えがあります:フィルターがどのインスタンスを処理する必要があるかを知る必要がある場合、インスタンスの周りに方法はありません(ある種の奇妙な反射マジックを除き、instanceof)。しかし、迅速でダーティなソリューションのために今保存しておいた時間は、製品のライフサイクルの後で、設計の誤りによるバグの修正に費やされています。
Thomas Junk、

@MichaelEricOberlin:だから、トーマスを正しく聞くと、あなたの選択はデザインを変更するか、そのままにするかのようです。
Robert Harvey

@RobertHarveyはこのように言います、それはtautologicに聞こえます;)彼がリファクタリングしたくない場合、現在の道を行く以外に選択肢はありません。
Thomas Junk、

2

実際の内容を知らずに特定の推奨事項を提示することは困難ですが、次のようにSpecificItemなりalso()ます。

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


それはかなり賢明で、結局それを行うためにリファクタリングするかもしれません。
Michael Eric Oberlin

1

代替:

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

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

public class MyStream extends Stream
{
    //...

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

3
それは方法ではありません。悪いデザインを悪化させることによって改善することです。
Thomas Junk

ここでトーマス・ジャンクに同意する必要があります。ステップ3は基本的に、私がすでに行っていることを実行します。そして、再びinstanceofを使用せずに、メソッドがこれをどのように解決するかを確認できません。
Michael Eric Oberlin

1
@MichaelEricOberlin:これはまさに私が心配していたことです。誰かの「ベストプラクティス」のアイデアを満たすためだけにRube Goldbergを作成する。
Robert Harvey

1

ここでも明らかなものを見逃さないようにしたいのですが(@Tristan Burnsideのコメントで示唆されているように)、なぜ最初にSpecificItem.method()呼び出すことができないのalso()ですか?

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

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

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


-1

これが私のアプローチです:

stuff.stream().filter(item -> SpecificItem.class.isInstance(item)).map(item  -> SpecificItem.class.cast(item)).forEach(item -> item.also());
stuff.stream().forEach(item -> item.method());

instanceof、明示的なキャストはありません(実際には明示的なキャストですが、メソッド呼び出しによってマスクされます)。これはできる限り良いと思います。


1
これはオリジナルの可読性の低いバージョンではないですか?
grahamparks

まあ、それは実際にはタイプセーフな方法で同じことを行います。コードのコンテキストによっては、よりシンプルになる場合があります。これを使用して、forEachパーツを除いて、ジェネリックリスト内の特定のタイプをフィルタリングします。
2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.