2017-02-05 3 views
3

Je comprends que l'une des raisons Les caractères génériques inférieurs existent pour qu'une collection ne soit pas immuable lors de l'ajout de nouveaux éléments.Joker Wildcards Java - Accès aux méthodes

E.g.

List<? extends Number> obj = new ArrayList<>();//Now this list is immutable 
obj.add(new Integer(5));//Does not compile 
List<? super Number> objTwo = new ArrayList<>();//This list is mutable 
objTwo.add(new Integer(5));//Compiles 

Ce qui suit ne compile pas parce que j'ai essayé d'obtenir la valeur longue des nombres.

Q1: Quelles méthodes pourrais-je utiliser? Seuls les objets méthodes ?:

public void testLowerBounds(List<? super Number> numbers){ 
     if (!numbers.isEmpty()){ 
      System.out.println(numbers.get(0).longValue());//Does not compile 
     }  

}

Comment ma question est arrivé: Je suis en train d'apprendre sur les flux et le livre indique la méthode de flux suivant:

Optional<T> min(Comparator<? super T> comparator) 

et met en œuvre comme suit:

Stream<String> s = Stream.of("monkey", "ape", "bonobo");  
Optional<String> min = s.min((s1, s2) -> s1.length()—s2.length()); 

Q2: Comment le comparateur est-il autorisé à utiliser des méthodes de chaîne lorsqu'il est utilisé? Si je devais répondre Q2: je dirais qu'optionnel est en spécifiant "Vous devez me passer une implémentation de Comparator qui a un type générique" String "ou quelque chose qui implémente" String ". cela?

dans l'attente de votre réponse.

+0

Cela semble être deux questions différentes. Et en ce qui concerne la première hypothèse, lorsque le compilateur empêche l'ajout à la liste, il ne signifie pas que la liste est immuable. Le compilateur obéit simplement aux règles de vérification de type statique du langage. – manouti

+0

De même, 'List' n'a pas de méthode' longValue() '. – manouti

+0

Salut AR.3 merci pour les commentaires. J'ai changé la question comme vous aviez raison, je vérifiais une méthode de liste au lieu du contenu. Ce sont en effet deux questions différentes, je voulais juste expliquer l'origine de la question. Je comprends ce que vous dites sur immutable, mais maintenant il est logiquement immuable que le compilateur ne vous laissera pas ajouter à la liste. Avez-vous des réponses à ces questions maintenant? –

Répondre

1

tout d'abord, vous ne devriez pas confondre les paramètres de type générique avec la mutabilité. Avoir un caractère générique dans un type d'élément de » List ne pas empêcher des modifications, il ne impose quelques pratiques restrictions à ce que vous pouvez faire avec le lis t. Avoir une liste déclarée comme List<? extends Number> implique que la liste référencée a un type d'élément réel de Number ou une sous-classe de Number, par exemple, . ce pourrait être un List<Integer> ou List<Double>. Vous ne pouvez donc pas ajouter une instance arbitraire Number car vous ne pouvez pas savoir si elle est compatible avec le type d'élément réel.

Mais vous pouvez toujours ajouter null, car la référence null est connue pour être compatible avec tous les types de référence. En outre, vous pouvez toujours supprimer des éléments d'une liste, par ex. appelez remove ou clear sans problèmes. Vous pouvez également appeler des méthodes comme Collections.swap(list, index1, index2), ce qui est intéressant car il ne serait pas légal d'appeler list.set(index1, list.get(index2)) en raison de règles formelles concernant les types génériques, mais en passant la liste à une autre méthode qui pourrait utiliser une variable de type non générique pour représenter l'élément de la liste type travaux. C'est évidemment correct, car il ne fait que définir des éléments issus de la même liste, qui doit être compatible.

De même, si vous avez un Comparator<Number>, vous pouvez appeler Collections.sort(list, comparator), en tant que comparateur qui peut gérer des nombres arbitraires, il sera capable de gérer tous les nombres qui sont réellement stockés dans la liste.Pour résumer, ayant ? extends dans le type d'élément d'une collection, et non empêche les modifications.


Comme dit, vous ne pouvez pas insérer de nouveaux éléments arbitraires dans une liste dont le type élément réel pourrait être une sous-classe inconnue de la limite, comme avec List<? extends Number>. Mais vous avez la garantie d'obtenir une instance Number lors de la récupération d'un élément, car chaque instance de sous-type Number est également une instance de Number. Lorsque vous déclarez List<? super Number>, son type d'élément réel peut être Number ou un super type de Number, par ex. Object ou Serializable. Vous pouvez insérer des instances arbitraires Number, comme vous le savez, il sera compatible avec tout type d'élément réel de la liste, car il s'agit d'un super type de nombre. Lorsque vous récupérez une instance, vous savez seulement qu'il s'agit d'une instance de Object, car il s'agit du super type de toutes les instances. Pour comparer avec le cas ? extends, avoir une déclaration ? super n'empêche pas la lecture, cela impose seulement quelques limitations pratiques. Et de même, vous pouvez toujours le passer à Collections.swap, car, peu importe le peu que nous savons sur le type réel, l'insertion de ce que nous venons de récupérer de la même liste, fonctionne.

Dans votre deuxième question, vous confondez les côtés. Vous ne regardez pas maintenant la mise en œuvre de min, mais à l'appelant . La déclaration de min(Comparator<? super T> c) permet à l'appelant de passer tout comparateur paramétré avec T ou un super type de T. Donc, quand vous avez un Stream<String>, il est valide de passer un Comparator<String> à la méthode min, ce qui est exactement ce que vous implémentez via l'expression lambda (s1, s2) -> s1.length()—s2.length() (cependant, je préférerais Comparator.comparingInt(String::length)).

Dans l'implémentation de min, il n'y a en effet aucune connaissance de ce que soit T ou l'argument de type réel du Comparator. Mais il suffit de savoir que n'importe quel élément de flux de type T peut être passé à la méthode compare du comparateur, qui peut s'attendre à T ou à un super type de T.

+0

Merci @Holger. Vous avez eu de bonnes explications. J'ai particulièrement aimé la réponse à ma deuxième question (Nice lambda en passant, en utilisant des références de méthode). Je comprends votre point de vue sur la mutabilité. Avec la réponse à ma première question, vous avez dit que je n'avais accès qu'aux méthodes Object, ce à quoi je m'attendais car le type de référence pourrait être un objet car c'est l'information que vous avez à ce moment-là. –