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>
?
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>?
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.
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
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
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
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
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