2016-08-30 4 views
3

Lets sagen, ich habe diese drei Klassen (stark reduziert):Liste <Base>-<Long> Liste ... bedingter Guss

public interface Base 
{ 
    String getType(); 
} 

public class Object1 implements Base 
{ 
    String getType() { ... }; 

    long getSerialNr() { ... }; 
} 

public class Object2 implements Base 
{ 
    String getType() { ... }; 

    long getPartNr() { ... }; 
} 

Wie würde ich (Java 8-Streams verwendet wird) konvertiert eine List<Base> auf einen List<Long> mit bedingten Abgüsse ? Ich begann mit diesem:

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

... aber wie kann ich den zweiten Fall zu integrieren, in dem das Element eine Instanz von Object2 ist und ich möchte getPartNr zurückzukehren?

+1

Warum nicht einfach zu erklären 'lange getId()' im 'Base'-Schnittstelle? – Holger

+0

Das ist leider nicht möglich. Das ist nur ein vereinfachtes Beispiel für mein "Real World Problem", bei dem es sinnvoll ist, getId() nicht in der Schnittstelle zu haben. – paddy3k

Antwort

3

Unter der Annahme, dass die Anzahl der Klassen wachsen kann, Sie zu abstrahieren die Zuordnung des Typs Eigenschaft haben:

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

Die get Methode geht davon aus, dass die Prädikate sind gegenseitig ausschließende, was der Fall ist, wenn für die Prüfung diese nicht interface Typen.

Dann können Sie es mögen verwenden:

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

Sie könnten auch die get Inline, aber das ist nicht verbessert die Lesbarkeit:

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

Zunächst einmal gibt es keinen Punkt in der Schnittstelle Base, wenn es dort keine gemeinsame Methode gibt, die alle Klassen implementieren sollten. WENN du es so machen willst, dann wird es nicht nett sein. Sie sollten Ihren Code an den unter der ändern sollte es tun:

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

Das Beispiel wurde stark auf das Kernproblem reduziert ... Ich würde keine Schnittstelle ohne Methoden verwenden, die zwischen den Implementierungen natürlich geteilt werden. Danke für diesen Ausschnitt! – paddy3k

+0

Die anderen veröffentlichten Lösungen für dieses Problem sind zu komplex. Dies ist die einfachste und direkteste Lösung, die den anderen vorzuziehen ist. – Lii

1

Sie zu einer Gruppe von seinen Elementen Klasse des baseList versuchen könnten.

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

Danke, aber ich wollte eine Liste von Long-Werten anstelle einer Map mit 2 Einträgen (bzw. 2 getrennten Listen). – paddy3k

+1

ok, ich habe das Beispiel aktualisiert. Sie können die Zwischenergebnisse verknüpfen, um eine Operation zu erhalten, aber die Lesbarkeit leidet darunter. – mrt181

+0

Danke! Jetzt ist das die dritte Möglichkeit, um das gewünschte Ergebnis zu erhalten. Ich habe gerade angefangen, die Java8-Stream-API zu verwenden, und ich denke darüber nach, wie stark der Leistungseinfluss zwischen diesen 3 Antworten in diesem Thread ist. Es ist alles schön zu lesen statt der guten alten für (...) Schleife, aber ich denke es ist viel langsamer, nicht wahr? – paddy3k