web-dev-qa-db-fra.com

Pourquoi List <String> n'est pas acceptable en tant que List <Object>?

Considérez ci-dessous la méthode doSomething(List<Object>) qui accepte List<Object> Comme paramètre.

private void doSomething(List<Object> list) {
    // do something
}

Considérez maintenant l'extrait de code ci-dessous qui essaie d'appeler doSomething() où j'essaie de passer List<String> À doSomething()

List<Object> objectList;
List<String> stringList;

doSomething(stringList); // compilation error incompatible types
doSomething(objectList); // works fine 

Même en dessous du code génère une erreur de compilation

objectList = stringList;  // compilation error incompatible types

Ma question est pourquoi List<String> Ne peut pas être passé à une méthode qui accepte List<Object>?

31
user3374518

Cette question générique dans Java peut sembler déroutante pour quiconque n'est pas très familier avec les génériques car à première vue, il semble que String soit un objet donc List<String> peut être utilisé où List<Object> est requis mais ce n'est pas vrai. Cela entraînera une erreur de compilation.

Cela a du sens si vous allez plus loin car List<Object> peut stocker tout y compris String, Integer etc mais List<String> ne peut stocker que Strings.

Jetez également un œil à: Pourquoi ne pas hériter de List <T>?

10
Shishir Kumar

Parce que tandis que String étend Object, List<String> ne s'étend pas List<Object>

Mise à jour:
En général, si Foo est un sous-type (sous-classe ou sous-interface) de Bar, et G est une déclaration de type générique, ce n'est pas le cas que G<Foo> est un sous-type de G<Bar>.

En effet, les collections changent. Dans votre cas, si List<String> était un sous-type de List<Object>, des types autres que String peuvent y être ajoutés lorsque la liste est référencée à l'aide de son supertype, comme suit:

List<String> stringList = new ArrayList<String>;
List<Object> objectList = stringList;// this does compile only if List<String> where subtypes of List<Object>
objectList.add(new Object());
String s = stringList.get(0);// attempt to assign an Object to a String :O

et le compilateur Java doit empêcher ces cas.

Plus d'informations sur this Java Page du didacticiel.

43
Ahmad Y. Saleh

Vous pouvez mettre un objet d'un mauvais type dans la liste SI cela a fonctionné:

private void doSomething(List<Object> list) {
    list.add(new Integer(123)); // Should be fine, it's an object
}

List<String> stringList = new ArrayList<String>();
doSomething(stringList); // If this worked....
String s = stringList.get(0); // ... you'd receive a ClassCastException here
17
Marco13

La raison de ces limitations est liée à des considérations de variance.

Prenez le code suivant:

public void doSomething(List<Object> objects)
{
  objects.add(new Object());
}

En développant votre exemple, vous pouvez essayer de procéder comme suit:

List<String> strings = new ArrayList<String>();
string.add("S1");

doSomething(strings);

for (String s : strings)
{
  System.out.println(s.length);
}

Avec un peu de chance, il est évident pourquoi cela se briserait si le compilateur autorisait la compilation de ce code (ce qui n'est pas le cas) - un ClassCastException se produirait pour le deuxième élément de la liste lors de la tentative de transtypage du Object vers un String.

Pour pouvoir transmettre des types de collection généralisés, vous devez procéder comme suit:

public void doSomething(List<?> objects)
{
  for (Object obj : objects)
  {
    System.out.println(obj.toString);
  }
}

Encore une fois, le compilateur surveille votre dos et si vous deviez remplacer le System.out Par objects.add(new Object()) le compilateur ne permettrait pas cela parce que objects aurait pu être créé comme List<String>.

Pour plus d'informations sur la variance, voir l'article Wikipedia Wikipedia Covariance et contravariance

4
Nick Holt

On s'attend parfois à ce qu'un List<Object> serait un super-type d'un List<String>, car Object est un sur-type de String.

Cette attente provient du fait qu'une telle relation de type existe pour les tableaux:

Object[] est un sur-type de String[], car Object est un sur-type de String. (Ce type de relation est appelé covariance.)

La relation de super-sous-type des types de composants s'étend aux types de tableaux correspondants.

Une telle relation de type n'existe pas pour les instanciations de types génériques. (Les types paramétrés ne sont pas covariants.)

Vérifiez ici pour plus de détails

3

De Java Tutoriels de génériques:

Testons votre compréhension des génériques. L'extrait de code suivant est-il légal?

List<String> ls = new ArrayList<String>(); // 1
List<Object> lo = ls; // 2 

La ligne 1 est certainement légale. La partie la plus délicate de la question est la ligne 2. Cela se résume à la question: est une liste de chaînes une liste d'objets. La plupart des gens répondent instinctivement: "Bien sûr!"

Eh bien, jetez un œil aux quelques lignes suivantes:

lo.add(new Object()); // 3
String s = ls.get(0); // 4: Attempts to assign an Object to a String!

Ici, nous avons alias ls et lo. En accédant à ls, une liste de String, via l'alias lo, nous pouvons y insérer des objets arbitraires. En conséquence, ls ne détient plus uniquement des cordes, et lorsque nous essayons d'en tirer quelque chose, nous obtenons une grossière surprise.

Le compilateur Java empêchera cela de se produire bien sûr. La ligne 2 provoquera une erreur de temps de compilation.

Source: Génériques et sous-typage

2
Ankit Lamba

Si vous n'êtes pas sûr du type de données qu'il prendra, vous pouvez utiliser les génériques dans Java comme suit

public static void doSomething(List<?> data) {

}

public static void main(String [] args) {
    List<Object> objectList = new ArrayList<Object>();
    List<String> stringList = new ArrayList<String>();
    doSomething(objectList);
    doSomething(stringList);
}

Mais lors de l'utilisation des données, vous devrez spécifier le type de données approprié en tant que conversion de type

0
rsakhale