2015-08-25 2 views
9

Créer une classe commeLa vérification de type est-elle interrompue lors de la capture correspondante avec limite supérieure?

public class Play { 
    public static void main(String[] args) throws Exception { 
     outer(Integer.class, inner("abc")); 
    } 

    static <C> void outer(Class<C> c, List<? super C> s){ 
    } 

    static <C> List<C> inner(C c) { 
     return null; 
    } 
} 

et compile en Java 8! (Dans Eclipse 4.5 et JDK1.8_25) https://ideone.com/Q9JLHP

Dans Eclipse, toutes les bornes sont correctement inféré, mais comment outer est capture Supplier<? super Integer> jamais été satisfaite par l'argument Supplier<String> ?? Editer: clarifié c'est spécifique à Java 8 et a rendu l'exemple moins confus.

+0

@ bayou.io C'est juste un type générique arbitraire que j'ai choisi pour l'exemple. Je suis sûr que le compilateur ne le traite pas spécialement ici. Je vais mettre à jour l'exemple pour éviter cette confusion. –

Répondre

10

inner("abc") peut, à la discrétion du compilateur, être interprété comme un Supplier de tout supertype de String. - par exemple,

Supplier<Object> inner = inner("abc"); 

fonctionne très bien, parce que "abc" est aussi un Object. C'est ce qui se passe ici: inner vous renvoie un Supplier<Object>.

+0

Mais ce comportement semble être nouveau pour Java 8. Toute référence à pourquoi ce changement a été fait? Parce qu'il a cassé le contrôle de type sur 'assertThat()' de Hamcrest, qui est couramment utilisé. –

+1

Java 8 a [l'inférence de type cible] (http://openjdk.java.net/jeps/101), qui permet aux informations d'inférence de type de se propager dans l'AST, ainsi que vers le haut. C'est à peu près strictement nécessaire pour que les lambdas trouvent magiquement le bon type de contexte en général. –

+2

En plus d'aider les expressions lambda, elle résout les failles qui existaient depuis l'introduction des génériques, c'est-à-dire que vous ne pouviez pas écrire 'Liste l = Arrays.asList (42);' ou 'Liste l = condition? ...: Collections.emptyList(); ', etc. S'il brise le code existant, c'est le code existant qui est défectueux car le même code aurait passé le compilateur plus ancien quand le type déduit sera inséré manuellement, c'est-à-dire ici' outer (Integer.class, Play inner ("abc")); ' – Holger

1

L'inférence sur inner requiert que C soit un supertype de Integer et String. Qu'est-ce que C exactement? C'est une histoire compliquée. Les deux Integer et String sont Object, bien sûr. Mais les deux sont Serializable aussi! et Comparable<?> aussi ....

En fin de compte, cela n'a pas beaucoup d'importance; tout ce que nous devons savoir, c'est qu'il s'agit d'une «limite supérieure» de String et Integer, de quelque manière que ce soit defined.

+0

' C' ne doit pas être un supertype de 'Integer' et' String'. La signature 'List' dans' outer' doit juste contenir des éléments d'un certain type indéfini de 'Integer' et' String'. –