2017-06-14 4 views
8
public class Test { 

    static List<Object> listA = new ArrayList<>(); 

    public static void main(final String[] args) { 
     final List<TestClass> listB = new ArrayList<>(); 
     listB.add(new TestClass()); 

     // not working 
     setListA(listB); 

     // working 
     setListA(listB.stream().collect(Collectors.toList())); 

     System.out.println(); 
    } 

    private static void setListA(final List<Object> list) { 
     listA = list; 
    } 

} 

Warum funktioniert es mit Streams und funktioniert nicht für den einfachen Satz?Java Liste der spezifischen Klasse zur Liste von java.lang.Object hinzufügen funktioniert mit Java 8 Streams - warum?

+4

'setListA (Collections.unmodifiableList (ListeB))' auch ohne den Overhead der Schaffung eines Strom funktionieren würde. – Radiodef

+1

@Radiodef Das würde etwas tun, das * völlig anders ist als der gepostete Code. (Nicht im Zusammenhang mit der Typinferenz usw., aber wollte es erwähnen. Die Übergabe einer 'neuen ArrayList (listB)' wäre näher an dem, was der gepostete Code tut, und würde auch funktionieren) – Marco13

Antwort

8

Für den ersten Fall schlägt fehl, weil List<TestClass> kein Untertyp von List<Object> ist. 1

Für den zweiten Fall haben wir die folgende Methode Erklärungen:

interface Stream<T> { 
    // ... 
    <R, A> R collect(Collector<? super T, A, R> collector) 
} 

und:

class Collectors { 
    // ... 
    public static <T> Collector<T, ?, List<T>> toList() 
} 

Dies ermöglicht Java den generischen Typparameter aus dem Kontext abzuleiten. In diesem Fall wird List<Object> für R und Object für T gefolgert.

So kann Ihr Code entspricht dies:

Collector<Object, ?, List<Object>> tmpCollector = Collectors.toList(); 
List<Object> tmpList = listB.stream().collect(tmpCollector); 
setListA(tmpList); 

1. Siehe zum Beispiel here.

2. Siehe z.B. here oder .

+0

Bedeutet das dann, dass Problem ist eine implizite Umwandlung in "Objekt" im ersten "nicht funktionierenden" Fall? – Thomas

+2

@Thomas - Es ist einfach so, dass 'List ' kein Subtyp von 'List ' ist und somit die Zuweisung fehlschlägt. –

+1

Können Sie genauer erläutern, warum die Liste für R und Object für T abgeleitet wird? Besonders in dem Kontext, in dem 'listB.stream()' tatsächlich einen 'Stream 'zurückgibt, warum sollte' listB.stream(). Collect (...) 'stattdessen Object verwenden? –

2

Diese Linie

setListA(listB); 

nicht, weil List in Java funktioniert unveränderlich ist, was bedeutet, List<TestClass>hat sich nichtList<Object> wenn TestClassObject erstreckt. Weitere Details here

Diese Linie

setListA(listB.stream().collect(Collectors.toList())); 

funktioniert, weil Java infer Objekt für generische Art Sammler von dieser Methode Unterschrift setListA(final List<Object> list) und so kommt man eigentlich List<Object> es

+1

Ich mag Ihre Erklärung der Invarianz in diesem Fall. Ich habe mich wirklich gefragt, danke. – Thomas

1

die Typparameter von Java Generika ist Invarianz was bedeutet, dass es nicht als Typ Parameter Klassenhierarchie vererbt werden kann. Das gemeinsame Mutterprodukt von List<TestClass> und List<Object> ist List<?>.

generic inheritance

Sie detaillierte Antwort über Java generische Platzhalter von kotlin & java sehen können. zum Beispiel:

List<String> strings = new ArrayList<String>(); 
List<CharSequence> sequences = strings; // can't work 
List<? extends CharSequence> parent1 = strings; // works fine 
List<?> parent2 = strings; // works fine 
// ^--- is equaivlent to List<? extends Object> 

die Ströme Ansatz ist es, eine List<TestClass> zu List<Object> verwandeln. Wenn Sie wollen, funktioniert es ohne Umwandlung eines List zu einem anderen List per Stream.Ihre Methoden Signatur sollte wie unten sein, und die Collection#addAll tut es auch in Java:

List<?> listA = new ArrayList<>(); 

private static void setListA(List<?> list) { 
    listA = list; 
} 
Verwandte Themen