2016-01-07 1 views
2

Voici le code simpleJava 8 Types incompatibles

import java.util.ArrayList; 
import java.util.Collections; 
import java.util.HashMap; 
import java.util.Map; 

public class SimpleTest { 

    public static void main(String[] args) { 
    final ArrayList<Map<String, Object>> maps = newArrayList(
     createMap("1", "a", Collections.EMPTY_MAP, Collections.EMPTY_MAP), 
     createMap("2", "b", Collections.EMPTY_MAP, Collections.EMPTY_MAP), 
     createMap("3", "c", Collections.EMPTY_MAP, Collections.EMPTY_MAP) 
    ); 

    System.out.println(" maps = " + maps); 
    } 

    public static Map<String, Object> createMap(String value1, String value2, Map<String, Object> object1, Map<String, Object> object2) { 
     Map<String, Object> map = new HashMap<>(); 
     map.put("value1", value1); 
     map.put("value1", value1); 
     map.put("object1", object1); 
     map.put("object2", object2); 
     return map; 
    }  

    public static <E> ArrayList<E> newArrayList(E... elements) { 
    ArrayList<E> list = new ArrayList<E>(elements.length); 
    Collections.addAll(list, elements); 
    return list; 
    } 
} 

Lorsque des points JAVA_HOME à JDK 8 et j'utilise javac -source 1.7 SimpleTest.java je reçois

SimpleTest.java:9: error: incompatible types: ArrayList<Map> cannot be converted to ArrayList<Map<String,Object>> 
     final ArrayList<Map<String, Object>> maps = newArrayList(
                   ^

Lorsque j'utilise -source 1.8 ou pas tout option -source fonctionne ok. Maintenant, lorsque JAVA_HOME pointe vers JDK 7, le code compile toujours si j'utilise -source 1.7 ou non.

Dans un premier temps cette question était d'un morceau de logiciel où fichier POM a une <source> et <target> ensemble à 1.7 et la construction échouait sur JDK 8 mais était ok sur JDK 7.

Maintenant pour la question - ce qui cause ça arrive? Cela me semble être un important désagrément. Pourquoi compiler sur JDK 8 avec source mis à 1.7 échoue?

+1

Alors, dites-vous que ce code compile avec JDK 7 mais pas avec JDK 8? – Tunaki

+0

Oui, exactement cela. Plus encore, les paramètres du compilateur dans Maven mis à la source étant de 1,7 dans les deux cas – EvgeniySharapov

+1

Veuillez définir explicitement ce que nous comparons ici. Compilez-vous avec un compilateur JDK 8 ou 7? Ou compilez-vous avec un compilateur JDK 8 avec la source 1.8 pour Maven et le compilateur JDK 7 avec la source 1.7? De plus, le code que vous avez posté n'a pas de 'java.util.ArrayList ' (avec des types bruts) donc je ne suis pas sûr de la façon dont vous avez eu cette erreur. – Tunaki

Répondre

4

Vous pouvez simplifier le code à un exemple autonome qui n'a pas besoin de bibliothèques 3ème partie:

public class Test2 { 
    @SafeVarargs 
    static <T> ArrayList<T> newArrayList(T... arg) { 
     return new ArrayList<T>(Arrays.asList(arg)); 
    } 
    private final List<Map<String, Object>> maps = newArrayList(
     createMap(null, Collections.EMPTY_MAP) 
    ); 

    public static Map<String, Object> createMap(String id, Map<String,String> m) { 
     return null; 
    } 
} 

Cela peut être compilé en utilisant javac de jdk1.7, mais pas avec javac de jdk1. 8 en utilisant -source 1.7.

Le point ici est que javac de jdk1.8 est encore un compilateur différent de celui inclus dans la version précédente et l'option -source 1.7 ne lui dit pas d'imiter le comportement de l'ancienne implémentation mais d'être compatible avec Java 7 spécification. Si l'ancien compilateur a un bug, le compilateur plus récent n'a pas à essayer de reproduire le bug.

Étant donné que le code utilise Collections.EMPTY_MAP plutôt que Collections.<String,String>emptyMap(), le type brut Map sera transmis à createMap, ce qui en fait une invocation sans contrôle ayant le type de résultat brut Map.

C'est mandaté par JLS §15.12.2.6:

Le type de résultat de la méthode choisie est déterminée comme suit:

  • Si la méthode choisie est déclarée avec un type de retour vide, le le résultat est nul. Sinon, si une conversion non contrôlée était nécessaire pour que la méthode soit applicable, alors le type de résultat est l'effacement (§4.6) du type de retour déclaré de la méthode.

...

Il semble que ce comportement n'a pas été (complètement) mis en œuvre javac de jdk1.7. Fait intéressant, il le fera correctement lors de l'ajout d'un paramètre de type comme

public static <T> Map<String, Object> createMap(String id, Map<String,String> m) 

ce qui en fait une méthode générique. Ensuite, jdk1.7 produira la même erreur.Mais la règle citée s'applique à toutes les invocations de méthode.


En revanche, le fait que la compilation avec Java 8 conformité réussit découle de la nouvelle inférence de type cible, ce qui a des règles totalement différentes.

+0

Je suppose, j'ai édité la question pendant que vous posiez votre réponse avec la suggestion. Une question qui reste est pourquoi '-source 1.8' fait compiler le compilateur JDK 8? – EvgeniySharapov

+1

C'est vrai, j'ai vu le montage après avoir soumis la réponse. Mais la dernière phrase le résume, les règles Java 8 sont totalement différentes. La signature générique de l'appel 'newArrayList' est dérivée du type cible. Notez que lorsque vous changez l'invocation en 'Test2. > newArrayList (...)' cela fonctionnera également dans Java 7 de manière conforme, bien qu'il y ait encore quelques différences subtiles dans la façon dont cela fonctionne dans Java 8. Expliquer complètement les règles Java 8 dépasserait la portée de la réponse. [Cette réponse] (http://stackoverflow.com/a/26285613/2711488) explique certaines différences. – Holger

+0

Merci de la référence à cette réponse. – EvgeniySharapov