Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Generics ... problème bizzare

7 réponses
Avatar
dawamli
Bonjour

J'ai une classe qui s'appelle NodeContainer et qui contient un object du
type Node

Cette classe NodeContainer est utilisée à de nombreux endroits.

J'aimerais me servir des génériques et définir NodeContainer comme

class NodeContainer<? extends Node> {

j'ai donc modifié la classe. Cela marche, même si bien souvent je ne crée
que des NodeContainer sans spécificer le type.

Par contre, en quelques endroits très précis, cela ne marche pas, par
exemple quand je fais :

NodeContainer node = agh.getNodeContainer(2);
for (LinkContainer lc : node.getConnectedLinks()) {
...
}

mais cela marche si je change en

NodeContainer<?> node = agh.getNodeContainer(2);
for (LinkContainer lc : node.getConnectedLinks()) {
...
}


Est-ce que quelqu'un sait pourquoi dans ce cas précis, il faut indiquer la
wildcard <?> ?

7 réponses

Avatar
ToOmS
On 5 déc, 15:13, "dawamli" wrote:
Bonjour

J'ai une classe qui s'appelle NodeContainer et qui contient un object du
type Node

Cette classe NodeContainer est utilisée à de nombreux endroits.

J'aimerais me servir des génériques et définir NodeContainer comme

class NodeContainer<? extends Node> {

j'ai donc modifié la classe. Cela marche, même si bien souvent je ne c rée
que des NodeContainer sans spécificer le type.

Par contre, en quelques endroits très précis, cela ne marche pas, par
exemple quand je fais :

NodeContainer node = agh.getNodeContainer(2);
for (LinkContainer lc : node.getConnectedLinks()) {
...

}

mais cela marche si je change en

NodeContainer<?> node = agh.getNodeContainer(2);
for (LinkContainer lc : node.getConnectedLinks()) {
...

}

Est-ce que quelqu'un sait pourquoi dans ce cas précis, il faut indiquer la
wildcard <?> ?


Bonjour,

Il faut savoir une chose TRES importante, avec les Generics : après la
compilation, le type étendu n'est pas conservé, c'est le type de base
qui est casté.
Donc, si #getConnectedLinks est une méthode de NodeContainer mais pas
Node... ça plante !

Faut le savoir, hein !

Avatar
ToOmS
On 5 déc, 15:13, "dawamli" wrote:
Bonjour

J'ai une classe qui s'appelle NodeContainer et qui contient un object du
type Node

Cette classe NodeContainer est utilisée à de nombreux endroits.

J'aimerais me servir des génériques et définir NodeContainer comme

class NodeContainer<? extends Node> {

j'ai donc modifié la classe. Cela marche, même si bien souvent je ne c rée
que des NodeContainer sans spécificer le type.

Par contre, en quelques endroits très précis, cela ne marche pas, par
exemple quand je fais :

NodeContainer node = agh.getNodeContainer(2);
for (LinkContainer lc : node.getConnectedLinks()) {
...

}

mais cela marche si je change en

NodeContainer<?> node = agh.getNodeContainer(2);
for (LinkContainer lc : node.getConnectedLinks()) {
...

}

Est-ce que quelqu'un sait pourquoi dans ce cas précis, il faut indiquer la
wildcard <?> ?


Bonjour,

Il faut savoir une chose TRES importante, avec les Generics : après la
compilation, le type étendu n'est pas conservé, c'est le type de base
qui est casté.
Donc, si #getConnectedLinks est une méthode de NodeContainer et
qu'elle surcharge
celle de Node... ça plante, parce que c'est celle de Node qui sera
effectivement exécutée !

Faut le savoir, hein ! Ca peut effectivement créer de drôles de
surprises...

Avatar
ToOmS
On 5 déc, 16:09, ToOmS wrote:

Il faut savoir une chose TRES importante, avec les Generics : après la
compilation, le type étendu n'est pas conservé, c'est le type de base
qui est casté.
Donc, si #getConnectedLinks est une méthode de NodeContainer et
qu'elle surcharge
celle de Node... ça plante, parce que c'est celle de Node qui sera
effectivement exécutée !

La solution (j'allais oublier) consiste à forcer le cast (même si cela

parait stupide) au moment de l'appel de la méthode surchargée, pour
signifier à la JVM que c'est bien la version de la classe étendue que
l'on souhaite exécuter.

Bonne continuation !

Avatar
dawamli
Merci, mais je crois que mon problème n'a pas été compris (je ne l'ai pas
décrit très clairement il est vrai)

la class NodeContainer n'étend pas Node, mais elle contient un objet de type
Node. Ou plutot une sous-classe de Node.

Par exemple, on a un NodeContainer<DefaultNode> ou un NodeContainer<XMLNode>
etc.

et DefaultNode extends Node, XMLNode extends Node

Dans mon cas, le fait de transformer la declaration de la classe
NodeContainer de

classNodeContainer extends AbstractContainer {

en

class NodeContainer<? extends Node> extends AbstractContainer {

fait qu'il me faut modifier mon code à plusieurs endroits, et spécifier la
wildcard <?> après NodeContainer, par exemple :

NodeContainer nodeContainer = agh.getNodeContainer(2);
for (LinkContainer lc : nodeContainer.getConnectedLinks()) {
...
}

doit-être modifié en

NodeContainer<?> nodeContainer = agh.getNodeContainer(2);
for (LinkContainer lc : nodeContainer.getConnectedLinks()) {
...
}


alors que si je fais

NodeContainer nodeContainer = new NodeContainer()

cela ne change rien. C'est donc spécialement lors de l'utilisation des
foreach/iterator...



une idée ?


"ToOmS" a écrit dans le message de news:

On 5 déc, 16:09, ToOmS wrote:

Il faut savoir une chose TRES importante, avec les Generics : après la
compilation, le type étendu n'est pas conservé, c'est le type de base
qui est casté.
Donc, si #getConnectedLinks est une méthode de NodeContainer et
qu'elle surcharge
celle de Node... ça plante, parce que c'est celle de Node qui sera
effectivement exécutée !

La solution (j'allais oublier) consiste à forcer le cast (même si cela

parait stupide) au moment de l'appel de la méthode surchargée, pour
signifier à la JVM que c'est bien la version de la classe étendue que
l'on souhaite exécuter.

Bonne continuation !

Avatar
ToOmS
On 5 déc, 16:46, "dawamli" wrote:
Merci, mais je crois que mon problème n'a pas été compris (je ne l'a i pas
décrit très clairement il est vrai)

la class NodeContainer n'étend pas Node, mais elle contient un objet de type
Node. Ou plutot une sous-classe de Node.

Par exemple, on a un NodeContainer<DefaultNode> ou un NodeContainer<XMLNod e>
etc.

et DefaultNode extends Node, XMLNode extends Node

Dans mon cas, le fait de transformer la declaration de la classe
NodeContainer de

classNodeContainer extends AbstractContainer {

en

class NodeContainer<? extends Node> extends AbstractContainer {

fait qu'il me faut modifier mon code à plusieurs endroits, et spécifie r la
wildcard <?> après NodeContainer, par exemple :

NodeContainer nodeContainer = agh.getNodeContainer(2);
for (LinkContainer lc : nodeContainer.getConnectedLinks()) {
...

}

doit-être modifié en

NodeContainer<?> nodeContainer = agh.getNodeContainer(2);
for (LinkContainer lc : nodeContainer.getConnectedLinks()) {
...

}

alors que si je fais

NodeContainer nodeContainer = new NodeContainer()

cela ne change rien. C'est donc spécialement lors de l'utilisation des
foreach/iterator...

une idée ?

"ToOmS" a écrit dans le message de news:

On 5 déc, 16:09, ToOmS wrote:

Il faut savoir une chose TRES importante, avec les Generics : après la
compilation, le type étendu n'est pas conservé, c'est le type de bas e
qui est casté.
Donc, si #getConnectedLinks est une méthode de NodeContainer et
qu'elle surcharge
celle de Node... ça plante, parce que c'est celle de Node qui sera
effectivement exécutée !


La solution (j'allais oublier) consiste à forcer le cast (même si cela
parait stupide) au moment de l'appel de la méthode surchargée, pour
signifier à la JVM que c'est bien la version de la classe étendue que
l'on souhaite exécuter.

Bonne continuation !


(re)Bonjour,

Vous pouvez tout à fait définir une classe générique, mais lors de
l'instanciation, vous devez en fixer le type.
Or la syntaxe <? extends type> est une définition, elle ne correspond
pas à une déclaration, c'est pour quoi elle vous est refusée dans la
déclaration d'une variable.

NodeContainer<LinkContainer> nodeContainer = agh.getNodeContainer(2);
for (LinkContainer lc : nodeContainer.getConnectedLinks()) {
...

}



Avatar
ToOmS
On 5 déc, 17:22, ToOmS wrote:

Vous pouvez tout à fait définir une classe générique, mais lors de
l'instanciation, vous devez en fixer le type.
Or la syntaxe <? extends type> est une définition, elle ne correspond
pas à une déclaration, c'est pour quoi elle vous est refusée dans la
déclaration d'une variable.

NodeContainer<LinkContainer> nodeContainer = agh.getNodeContainer(2);

for (LinkContainer lc : nodeContainer.getConnectedLinks()) {
...

}



En fait, la syntaxe déclarative ici (NodeContainer<?>) a pour but de
vous permettre d'affecter un type générique DE LA CLASSE COURANTE à la
classe générique NodeContainer. Par exemple si la classe courante est
du genre NodeContainerContainer<? extends Node>


Avatar
Maxime Daniel
Il faudrait avoir un extrait de votre source plus grand pour être plus
précis, mais...
NodeContainer est un raw type. Les raw types servent en Java 1.5 et au-
delà à permettre une migration "en douceur" (sic!) qui mélange du code
d'avant les génériques avec du code avec les génériques. Leur
compréhension fine est cauchemardesque. Les effets de bord nombreux et
pas toujours heureux.
Donc, si vous voulez passer aux génériques et faire de NodeContainer
une classe générique, il faut l'utiliser correctement et la paramétrer
dans toutes les déclarations.
NodeContainer<?> est une classe générique, paramétrée par un type do nt
on ne sait rien. NodeContainer est un raw type, qui a une parenté
avec, mais est différent de NodeContainer.

Certains compilateurs vont au-delà de ce qui est requis par la
spécification Java et émettent des avertissements pour les
utilisations de raw types. Par exemple Eclipse.

Bon courage.