2016-08-30 8 views
3

は、私は、これらの3つのクラス(強く減少)を考えてみましょう?私はこれを開始しました:一覧<Base> ...条件キャスト

List<Long> result = baseList.stream().filter(Objects::nonNull) 
    .filter(Object1.class::isInstance) 
    .map(Object1.class::cast) 
    .map(o -> o.getSerialNr()) 
    .collect(Collectors.toList()); 

...しかし、どのように私は要素がObject2のインスタンスである第二のケースを、統合し、私はgetPartNrを返すようにしたいですか?成長するクラスの数は、あなたがプロパティに抽象化するタイプのマッピングを有することができると仮定すると

+1

単に 'Base'インタフェースで' long getId() 'を宣言してみませんか? – Holger

+0

残念ながらそれは不可能です。それはインターフェイスでgetId()を持たないのが理にかなっている私の「現実世界の問題」の単純すぎる例です。 – paddy3k

答えて

3

static Map<Predicate<Base>,Function<Base,Long>> ACCESSORS; 
static { 
    Map<Predicate<Base>,Function<Base,Long>> m=new HashMap<>(); 
    m.put(Object1.class::isInstance, base -> ((Object1)base).getSerialNr()); 
    m.put(Object2.class::isInstance, base -> ((Object2)base).getPartNr()); 
    ACCESSORS=Collections.unmodifiableMap(m); 
} 
static Stream<Long> get(Base b) { 
    return ACCESSORS.entrySet().stream() 
      .filter(e -> e.getKey().test(b)) 
      .map(e -> e.getValue().apply(b)); 
} 

get方法は、述語がためにテストケースがある、相互に排他的であることを前提としていこれらの非interfaceタイプです。

次に、あなたのようにそれを使用することができます。

List<Long> result = baseList.stream() 
    .flatMap(YourClass::get) 
    .collect(Collectors.toList()); 

ます。またgetメソッドをインライン化できますが、それは読みやすさを改善していない:すべての

List<Long> result = baseList.stream() 
    .flatMap(b -> ACCESSORS.entrySet().stream() 
     .filter(e -> e.getKey().test(b)) 
     .map(e -> e.getValue().apply(b))) 
    .collect(Collectors.toList()); 
2

まず、にはポイントがありませんinterface Baseに、すべてのクラスを実装する共通メソッドが存在しない場合は、ベース。あなたがそれをやりたいのなら、それはうまくいかないでしょう。

List<Long> aList = baseList.stream().filter((Objects::nonNull) 
     .filter(obj->(obj instanceof Object1 || obj instanceof Object2)) 
     .map(num-> (num instanceof Object1) ? ((Object1) num).getPratNr() : ((Object2) num).getPratNr()) 
     .collect(Collectors.toList()); 
+0

この例は、コアの問題に大きく縮小されました...もちろん、実装間で共有されるメソッドを持たないインターフェイスは使用しません。そのスニペットをありがとう! – paddy3k

+0

この問題の他の公開ソリューションは複雑です。これは最も簡単で最も直接的な解決方法であり、他の方法よりも好ましい方法です。 – Lii

1

基本リストをその要素クラスでグループ化すると、次のようにコードを変更する必要があります。

public class MyTest { 

    @Test 
    public void java8_listbase_to_listlong_conditional_cast() { 
    List<Base> baseList = new ArrayList<>(); 
    baseList.add(new Object1()); 
    baseList.add(new Object2()); 

    Map<?, List<Base>> group = baseList.stream() 
     .filter(Objects::nonNull) 
     .collect(Collectors.groupingBy(key -> key.getClass())); 

    Stream<Long> object1stream = group.get(Object1.class) 
     .stream() 
     .map(Object1.class::cast) 
     .map(Object1::getSerialNr); 

    Stream<Long> object2stream = group.get(Object2.class) 
     .stream() 
     .map(Object2.class::cast) 
     .map(Object2::getPartNr); 

    List<Long> longList = Stream.concat(object1stream, object2stream).collect(Collectors.toList()); 

    assertArrayEquals(new Long[] {0l, 1l}, longList.toArray()); 
    } 



    public interface Base { 
    } 

    public class Object1 implements Base { 
    long getSerialNr() { return 0L; }; 
    } 

    public class Object2 implements Base { 
    long getPartNr() { return 1L; }; 
    } 
} 
+0

ありがとうございましたが、私は2つのエントリ(2つの分離されたリストを含む)を持つマップではなく、長い値のリストを求めました。 – paddy3k

+1

私は例を更新しました。中間結果をインライン化して1つの操作を得ることはできますが、可読性には問題があります。 – mrt181

+0

ありがとうございました!これで3つ目の可能性が得られました。私はちょうどJava8ストリームAPIを使い始めました。このスレッドの3つの回答の間にパフォーマンスの影響がどれくらいあるかを考えます。良い(...)ループの代わりに読むのはすごくいいですが、それはずっと遅いと思いますよね? – paddy3k