Erasure w strumieniu w Javie 8 a Javie 12

0

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.

0

Nic mi nie wiadomo żeby były jakies zmiany w generykach miedzye 8 a 12. Sa jakies plany związane z Project Valhalla ale jeszcze nie jest to włączone do JDK

0

To nie jest chyba błąd w kompilacji, a jakiś checkstyle. Na Javie 8 też działa:
https://ideone.com/AJiabj

0

Wkleiłem cały kod błędu i jasno widać, że się nie kompiluje. Tym bardziej podejrzewam buga w kompilatorze, skoro gdzieś indziej działa.

0

Nie wiem czym to kompilujesz ale generalnie cannot reproduce na żadnym JDK które mam pod ręką. Edit: to wygląda na jakiś bug w starej wersji JDK, wynikający z użycia RawTypes (new HashMap() bez parametrów).

0

Możliwe, że w którymś update Javy 8 przestało się to kompilować.

0

Potwierdzam problem z JDK 1.8.0_162 ;)

1

Oracle mówi, że zna problem od 2016... screenshot-20191029144530.png
Javac Throws "error: incompatible types: cannot infer type-variable(s)" with Valid Inferences (Doc ID 2171428.1)

0

@yarel: O to dokładnie mi chodziło, teraz mam podpórkę i wyjaśnienie, co się dzieje! Dzięki wielkie!

@Shalom:
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)
Na Windowsie 10 x64.
Możesz też spróbować tutaj: https://www.compilejava.net/

1 użytkowników online, w tym zalogowanych: 0, gości: 1