4
votes

Comment ajouter des valeurs pour la carte générique java avec un "?" Indéterminé type de valeur

J'ai vu ce genre de déclaration dans l'exemple jdk 8:

    map.put("abc", Optional.of(5));
    map.put("kk", "xyz");

Mais quand j'ai essayé d'ajouter de la valeur à "map", je n'ai pas réussi:

XXX

Les deux ne parviennent pas à se compiler. Je souhaite savoir:

(1) Que signifie "?" indiquer dans la déclaration de carte ci-dessus?

(2) Comment donner des valeurs à cette "carte"?


1 commentaires

? est le type inconnu est java. Vous ne pouvez pas ajouter à ce qui est inconnu.


3 Réponses :


8
votes

Map est un type abstrait. Si une variable a ce type, elle pourrait référencer un objet avec l'un des types suivants (et d'autres).

  • HashMap
  • TreeMap
  • HashMap>
  • TreeMap

De toute évidence, les possibilités sont infinies. Si vous avez une variable de ce type, vous savez qu'elle fait référence à une carte dont les clés sont String , mais vous ne savez vraiment rien d'autre. En particulier, vous ne savez pas avec quel type d'objet vous vous retrouverez lorsque vous effectuez un get sur cette Map .

Plus important encore, vous ne pourrez jamais mettre quoi que ce soit dans la carte, sans une sorte d'opération de casting désagréable et dangereuse. Le compilateur vous arrêtera. Donc, dans les exemples que vous avez donnés -

  • map.put ("abc", Optional.of (5)); ne sera pas compilé, car map pourrait être un HashMap , dans laquelle vous ne pouvez pas mettre un Facultatif .
  • map.put ("kk", "xyz"); ne sera pas compilé, car map pourrait être un TreeMap < / code>, dans lequel vous ne pouvez pas insérer de String .

Les exceptions seraient null , ou toute valeur provenant de la carte elle-même - voir l'excellente réponse d'Andy Turner pour plus de détails sur ces possibilités.

En bref, si vous avez une variable de type Map , les opérations que le compilateur vous laissera faire sont un peu limitées. Vous ne pouvez rien mettre dans la carte, à moins qu'il ne soit null ou qu'il soit déjà dans la carte. Tout ce que vous pouvez faire est d'obtenir des valeurs de la carte et de supprimer des valeurs de la carte.

Donc, utiliser une variable Map est très limitatif. Si tout ce que vous voulez faire avec la carte est de lire des valeurs à partir de celle-ci, c'est très bien, bien sûr. Mais ne vous attendez pas à pouvoir insérer des valeurs arbitraires dans la carte, sauf si vous utilisez une expression différente pour faire référence à la carte.


8 commentaires

Veuillez ne pas faire référence à la carte délimitée par des caractères génériques comme étant "en lecture seule". D'une part, vous pouvez supprimer des éléments; pour un autre, vous pouvez ajouter des éléments (voir ma réponse).


@Andy Vous avez tout à fait raison. J'étais juste en train de lire (et de voter) votre réponse. Je n'avais jamais pensé à ajouter une valeur qui provenait de la carte elle-même. Je vais reformuler cette partie.


Je me demande à quoi sert cette construction. Je pourrais imaginer une classe abstraite définissant une méthode avec ceci comme type de retour, mais toute implémentation que je peux proposer violerait soit le principe de substitution liskov, soit le principe ouvert-fermé. ( en.wikipedia.org/wiki/SOLID )


Eh bien, @tgr, vous pouvez écrire des méthodes qui traitent la carte en lecture seule. Par exemple, vous pouvez écrire une méthode qui imprime toutes les valeurs de la carte.


D'accord, l'impression est un mécanisme qui fonctionnerait sans prendre aucune hypothèse sur les types de valeur. Mais à part ça? Je ne vois pas beaucoup de valeur d'une carte en lecture seule, dont les valeurs doivent être converties pour une utilisation ultérieure.


@tgr Si vous développiez une bibliothèque pleine de méthodes utilitaires qui fonctionnent sur des cartes, vous pourriez utiliser ce type de construction dans une grande partie de l'API.


@tgr ? est simplement une forme abrégée de la limite supérieure ? étend l'objet . Si vous deviez définir une limite supérieure différente, par exemple Map > , vous pouvez utiliser les valeurs de cette carte sans conversion explicite. Ce ? a des utilisations très limitées - de la même manière que Object a des utilisations très limitées - ne signifie pas que les caractères génériques sont limités dans leur utilité.


@tgr, il est également intéressant de noter que les caractères génériques ne devraient à peu près apparaître que dans les paramètres de méthode, afin d'augmenter la flexibilité de l'API par rapport aux paramètres qu'elle accepte. Vous ne définiriez pas une classe avec un caractère générique, donc LSP et OCP ne sont pas vraiment pertinents.



6
votes

Question (1)

 List<? super Exception> l = new ArrayList<Object>();

Question (2)

à donner valeur à cette carte, vous utilisez soit la limite supérieure, soit la limite inférieure.

Ce qui suit sont des directives lors de l'utilisation de la limite supérieure ou inférieure

 List<? extends Exception> l = new ArrayList<RuntimeException>();

PECS en bref Produit (accès en écriture) - utilise Étend tandis que Consomme (accès en lecture) - utilise Super

 List<?> l = new ArrayList<String>();

Sortie

kk  xyz
abc Optional[5]

Process finished with exit code 0

Notes supplémentaires

Caractère générique sans limite ?

Map<String, ? super Object> map = new HashMap<>(3);//OK
    map.put("abc",Optional.of(5));
    map.put("kk","xyz");
map.forEach((k,v)->System.out.println(k+"\t"+v));

Caractère générique avec une limite supérieure ? étend le type

? extends Type   - is used for read access only
? super Type     - is used for write access only.

Caractère générique avec une limite inférieure ? super type

? is called wildcard generic type.It can be used with any type,
but you need to specify type fist as Map is an abstract class.


0 commentaires

6
votes

? est un type inconnu: vous ne savez pas exactement ce que c'est à ce point dans le code.

À cause de cela, vous ne pouvez pas ajouter en toute sécurité de valeur à la carte, avec quelques exceptions.

Vous pouvez ajouter un littéral null (car null peut être converti en n'importe quel type):

<T> void reAdd(Map<String, T> map) {
  T value = map.get("123");
  map.put("456", value);
}

// Call with
reAdd(map); // compiles

Vous pouvez également ajouter une valeur à la carte que vous obtenez à partir de la carte:

map.put("abc", null); // compiles


0 commentaires