ajouter de la nourriture dans une liste d'objets

Le problème exposé dans ce sujet a été résolu.

Bonjour, le problème est simple : je dispose d’un panier représenté par une liste d’objets. La nourriture est un objet. Donc, il devrait être possible d’ajouter de la nourriture au panier.

Mais le code ne compile pas et je ne comprends pas pourquoi…

import java.util.ArrayList;


public class Objet {
}

public class Nourriture extends Objet {
}

public class Panier {
    private ArrayList<Objet> elements = new ArrayList<Objet>();
    
    public void ajouterObjets(ArrayList<Objet> liste) {
        elements.addAll(liste);
    }
}


public class Main {
    public static void main(String[] args) {
        Panier panier = new Panier();
        ArrayList<Nourriture> nourriture = new ArrayList<Nourriture>();
        
        panier.ajouterObjets(nourriture);
    }
}

L’erreur : The method ajouterObjets(ArrayList<Objet>) in the type Panier is not applicable for the arguments (ArrayList<Nourriture>)

L’erreur est explicite mais comme Nourriture extends Objet, pourquoi ne caste t’il automatiquement?

Je pourrais faire une méthode ajouterNourriture… sauf que dans mon projet réel, cela reviendrait à ajouter une dizaine de méthode comme ça (et avec le même code interne donc ça n’a aucun sens)…

+0 -0

Salut,

En effet, quand tu as de la généricité comme ça, ce n’est pas possible d’appeler la méthode avec un autre type générique. Par contre, tu peux utiliser le joker « ? ».

    public void ajouterObjets(ArrayList<?> liste) {
        elements.addAll(liste);
    }

Cette méthode autorise toutes les listes de n’importe quel type. Tu peux aussi préciser ton besoin avec le mot clé extends.

    public void ajouterObjets(ArrayList<? extends Object> liste) {
        elements.addAll(liste);
    }

Ici, on accepte tous les types qui héritent de Object (donc tous les types).

Par contre, en règle général, le mieux est de préciser le bon type de généricité, sauf si tu souhaite mettre plusieurs types d’objets dans ton pannier. Tout dépends ton souhait. Si tu peux avoir autre chose que de la nourriture, avoir une classe mère entre Nourriture et ton autre type d’objet te permettra de faire une méthode acceptant une List<? extends TypeParent>.

À noter qu’il est déconseillé de mettre un type d’implémentation en paramètre de méthode. C’est-à-dire que cela est inutile de dire que tu souhaite une ArrayList, une simple List suffit (l’interface). Si un jour tu souhaite utiliser des LinkedList en lieu et place des ArrayList ce serait très simple à changer. Ci-dessous ta classe qui doit apriori fonctionner :

public class Panier {
    private List<Object> elements = new ArrayList<>();
    
    public void ajouterObjets(List<?> liste) {
        elements.addAll(liste);
    }
}

Si tu as d’autres questions…

À+

+0 -0

J’évite cette option car je préfère être certain que la liste nourriture ne contienne que des objet de type Nourriture, évitons de mélanger les salades et les CD dans le panier :)

Et puis en essayant ça donne une erreur : Type mismatch: cannot convert from ArrayList<Player> to ArrayList<GameObject>

+0 -0

Super, merci c’est top ! :)

Par contre j’ai encore une petite erreur après avoir remplacé The method addAll(Collection<? extends Objet>) in the type List<Objet> is not applicable for the arguments (List<capture#1-of ?>)

marius007

Dans ton message d’erreur, ça parle de Objet. C’est un type à toi, ou c’est une faute de frappe voulant dire Object ?

Tu peux donner ton code que je regarde ? Là je ne comprends pas tout. ;)

Ah je comprends j’ai tapé mon code en français et donc Objet =/= Object…

Ce code compile :

import java.util.ArrayList;


public class Objet {
}

public class Nourriture extends Objet {
}

public class Main {

    public static void main(String[] args) {
        Panier panier = new Panier();
        ArrayList<Nourriture> nourriture = new ArrayList<Nourriture>();
        
        panier.ajouterObjets(nourriture);
    }
}

public class Panier {
    private List<Object> elements = new ArrayList<>();
    
    public void ajouterObjets(List<?> liste) {
        elements.addAll(liste);
    }
}

Merci :)

Alors je reviens pour un souci très similaire, pourquoi cela ne fonctionne-t’il pas ?

J’essaye d’utiliser le joker car une méthode abstraite peut prendre plusieurs types d’arguments.

public abstract class MaClasse {
    public abstract void ajouterObjet(<?> objet);
}

public class ClasseFille extends MaClasse {
    @Override
    public abstract void ajouterObjet(Meuble objet) {...}
}

public class ClasseFille2 extends MaClasse {
    @Override
    public abstract void ajouterObjet(Nourriture objet) {...}
}

Le joker ne peut être utilisé que pour passer de la généricité à une autre classe (List<…> ou Collection<…> par exemple). Soit, pour utiliser de la généricité. Or, quand tu dis juste <?>, tu ne donne pas le type d’objet que tu veux.

Ici, ce qu’il faut que tu fasse, c’est de créer ton propre type générique T, et ensuite de dire quel type d’objet c’est dans les classes filles. :

public abstract class MaClasse<T> {
    public abstract void ajouterObjet(T objet);
}

public class ClasseFille extends MaClasse<Meuble> {
    @Override
    public abstract void ajouterObjet(Meuble objet) {...}
}

public class ClasseFille2 extends MaClasse<Nourriture> {
    @Override
    public abstract void ajouterObjet(Nourriture objet) {...}
}

En admettans que MaClasse n’est pas une classe abstraite, tu pourrai également faire :

MaClasse<Meuble> toto = new MaClasse<>();

Ou avoir une méthode accueillant MaClasse en paramètre :

public void maSuperMethode(MaClasse<?> maClasse) {
   //
}

J’espère avoir été clair. Si tu as des questions, n’hésite pas !

Merci ! Alors c’est dingue parce que le code compile si je ne définis pas la classe T, pourquoi….

public abstract class MaClasse<T> {
    public abstract void ajouterObjet(T objet);
}

public class ClasseFille extends MaClasse<Meuble> {
    @Override
    public void ajouterObjet(Meuble objet) {}
}

public class ClasseFille2 extends MaClasse<Nourriture> {
    @Override
    public void ajouterObjet(Nourriture objet) {}
}

public class Meuble {}
public class Nourriture {}

Si je dois justifer cette syntaxe à la défense, que devrais je dire de T et de MaClasse<T>?

PS: existe-t-il une notation plus standard que T?

+0 -0

En fait, T n’est pas une classe. T, c’est souvent par convention pour « Type », mais tu peux mettre ce que tu veux.

Ça veut dire, « j’aurai un type, lors de la définition de ma classe, je ne sais pas ce que c’est, mais je m’en fiche ». Et c’est au moment des instanciations ou des implémentations que tu précise quel type donner. Une fois un type fourni, il faut que tu imagines que ça ne fait que faire un rechercher / remplacer du Type (T par exemple), par celui que tu as fourni (Meuble ou Nourriture).

C’est exactement le fonctionnement de la classe ArrayList par exemple. Le type n’est pas connu au moment de l’implémentation des méthodes, mais ce n’est pas grave, pour stocker les objets, pas besoin de savoir ce que l’on va stocker. Et ensuite, en tant qu’utilisateur de la classe, tu précises ce que tu veux mettre dans la liste avec ArrayList<Nourriture> par exemple pour dire « je vais stocker de la Nourriture dans ma liste ».

C’est plus clair ?

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte