Cześć. Weźmy taki kod:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
List<String> strings = new ArrayList<>();
List<Item> items = strings.stream().map(tqsScore -> new Item(new HashMap())).collect(Collectors.toList());
}
}
class Item {
public Map<String, String> metadata;
Item(Map<String, String> metadata) {
this.metadata = metadata;
}
}
Jest klasa Item
, która ma pole typu generycznego (słownik ze stringa do stringa). Przyjmuje ten słownik przez konstruktor. Teraz w klasie Test
, w metodzie main
iteruję po kolekcji stringów (to akurat bez różnicy, może być absolutnie cokolwiek) i w metodzie map
tworzę nowy obiekt klasy Item
, ale jako parametr przekazuję raw type zamiast generycznego słownika.
Skompilowanie tego w Javie 8 rzuca błędem, rezultat tutaj:
/tmp/java_fR1LWz/Test.java:10: warning: [unchecked] unchecked method invocation: constructor <init> in class Item is applied to given types
List<Item> items = strings.stream().map(tqsScore -> new Item(new HashMap())).collect(Collectors.toList());
^
required: Map<String,String>
found: HashMap
/tmp/java_fR1LWz/Test.java:10: warning: [unchecked] unchecked conversion
List<Item> items = strings.stream().map(tqsScore -> new Item(new HashMap())).collect(Collectors.toList());
^
required: Map<String,String>
found: HashMap
/tmp/java_fR1LWz/Test.java:10: warning: [unchecked] unchecked method invocation: method map in interface Stream is applied to given types
List<Item> items = strings.stream().map(tqsScore -> new Item(new HashMap())).collect(Collectors.toList());
^
required: Function<? super T,? extends R>
found: Function<String,Item>
where T,R are type-variables:
T extends Object declared in interface Stream
R extends Object declared in method <R>map(Function<? super T,? extends R>)
/tmp/java_fR1LWz/Test.java:10: warning: [unchecked] unchecked call to <R,A>collect(Collector<? super T,A,R>) as a member of the raw type Stream
List<Item> items = strings.stream().map(tqsScore -> new Item(new HashMap())).collect(Collectors.toList());
^
where R,A,T are type-variables:
R extends Object declared in method <R,A>collect(Collector<? super T,A,R>)
A extends Object declared in method <R,A>collect(Collector<? super T,A,R>)
T extends Object declared in interface Stream
/tmp/java_fR1LWz/Test.java:10: error: incompatible types: Object cannot be converted to List<Item>
List<Item> items = strings.stream().map(tqsScore -> new Item(new HashMap())).collect(Collectors.toList());
^
1 error
4 warnings
Czyli kompilator krzyczy, że wprawdzie udaje się utworzyć Item
, ale potem dopasowuje lambdę jako zwracającą Object
, więc potem nie da się tego przypisać do List<Item>
..
Ale ten kod kompiluje się poprawnie w nowszej Javie, na przykład 12: https://ideone.com/2zYwZs
I pytanie: co dokładnie zmieniło się między tymi wersjami, że kod przedtem nie działał, a teraz już działa? Wydaje mi się, że jest to bug, wygooglałem podobną sytuację w https://coderanch.com/t/664412/java/Stream-collect-Error-incompatible-types i tam też jest sugestia, że to problem kompilatora, ale być może coś się pozmieniało w specyfikacji i stąd różnica.. Będę wdzięczny za jakieś rozjaśnienie.