7 votes

Différence entre l'utilisation de caractères génériques et la déclaration d'un type générique dans une méthode abstraite en Java.

J'essaie de comprendre les types génériques en Java, et en théorie, cela semble compréhensible, mais lorsque je dois l'appliquer à du code réel, j'ai des problèmes. Je veux déclarer une méthode abstraite qui retournera un type générique. Supposons que j'ai une interface vide appelée Magicable et que 2 classes l'implémentent : Magicien et Sorcière. Maintenant je me demande quelle est la différence entre ces 3 déclarations :

/*1*/protected abstract <T extends Magicable> List<T> getMagicables();
/*2*/protected abstract List<? extends Magicable> getMagicables();
/*3*/protected abstract List<Magicable> getMagicables();
  1. Dans le premier cas, j'ai un problème lorsque je veux implémenter le corps de cette méthode dans une classe qui étend la classe abstraite :

    @Override
    protected List<Magican> getMagicable() {..}

    J'ai un message d'avertissement :

    Sécurité du type : Le type de retour List<Magican> pour getMagicable() du type MagicanService nécessite une conversion non vérifiée pour être conforme à List<Magicable> du type MagicableService.

  2. Dans le deuxième cas, je n'ai pas cet avertissement, mais j'ai un problème dans la classe abstraite dans laquelle j'ai déclaré la méthode abstraite ci-dessus :

      public void <T extends Magicable> T getOneFromList() {
          List<T> list = getMagicables();
          //.....
      }

    Dans ce cas, j'ai une erreur de compilation dans l'appel getMagicables() :

    Erreur de type : impossible de convertir une liste<capture#2-of ? extends Magicable> en liste<T>.

  3. Le troisième cas provoque des erreurs de compilation dans les deux endroits du code mentionnés ci-dessus. Je ne pense pas que ce soit la bonne solution dans mon cas.

2voto

Max Points 431
  1. Premier cas

Il suffit de déclarer votre méthode avec :

    @Override
    protected <T extends Magicable> List<T> getMagicables() {
       List<T> list = ...
       return list
    }

Si tu veux vraiment ça :

    @Override
    protected List<Magican> getMagicable() {..}

Vous devrez peut-être déclarer votre T générique dans la définition de la classe.

     public abstract class AbstractKlass<T extends Magicable> {
        protected abstract List<T> getMagicables();
     }

puis dans votre sous-classe :

     public class MySubClass extends AbstractKlass<Magican> {

        @Override
        protected List<Magican> getMagicables() {
           ...
        }
     }
  1. Deuxième cas

L'erreur de compilation est normale car <? extends Magicable> de la signature de la méthode signifie que vous ne vous souciez pas de ce que contient votre liste à partir du moment où vous pouvez considérer ces éléments comme des Magicable. Lorsque vous faites un appel

    List<T> list = getMagicables();

Vous voulez prendre soin du type T sans le savoir. En d'autres termes, il y a 3 cas d'utilisation : T is Magicable (OK), T is Magician (Faux car getMagicables peut retourner une liste de Witch) et T is Witch (Faux aussi).

  1. Pourquoi j'utilise ? extends Magicable au lieu de simplement Magicable dans les listes

Parce que List<Magician> est un sous-type de List<? extends Magicable> mais pas un sous-type de List<Magicable> . Ceci est utile pour les paramètres des méthodes.

    public void doIt(List<? extends Magicable> list) {
         // you can't add a Magician here
    }

peut être utilisé comme

    List<Witch> list = ...
    doIt(list);

Mais si vous avez

    public void doIt(List<Magicable> list) {
         // you can add a Magician here
    }

Vous ne pouvez pas l'utiliser comme

    List<Witch> list = ...
    doIt(list); // compile error

1voto

Stefan Points 3223

Pour la partie du problème que vous nous avez montrée, la méthode /* 3 */ est suffisante, vous n'avez pas besoin des génériques pour cette partie de votre code. Mais vous devez respecter la substituabilité :

Vous obtenez l'erreur #1 parce que la méthode du sous-type restreint l'étendue du type de retour : a Magican es Magicable mais pas l'inverse. Les super-types de Magicable sont autorisées dans le sous-type. La méthode du sous-type doit être substituable à la méthode du super-type, ce qui n'est pas le cas dans votre exemple.

L'erreur dans #2 est due à la nature du joker ? : ? extends Magicable y T extends Magicable ne doivent pas nécessairement être du même type. Si T est déclaré dans la portée de la classe, par exemple, la classe Magican<T> implements Magicable<T> (bien sûr, votre interface doit déclarer T dans ce cas) toutes les occurrences de T dans votre type se réfèrent à la même classe.

1voto

Yogesh Badke Points 1851
public abstract class AbstractMagicable<T extends Magicable> {

    abstract List<T> getMagicables1();

    abstract List<? extends Magicable> getMagicables2();

    abstract List<Magicable> getMagicables3();
}

class MagicableWitch extends AbstractMagicable<Witch> {

    @Override
    List<Witch> getMagicables1() {
        return null;
    }

    @Override
    List<? extends Magicable> getMagicables2() {
        return getMagicables1();
    }

    @Override
    List<Magicable> getMagicables3() {
        return Collections.singletonList(new Witch());
    }   
}

class MagicableMagician extends AbstractMagicable<Magician> {

    @Override
    List<Magician> getMagicables1() {
        return null;
    }

    @Override
    List<? extends Magicable> getMagicables2() {
        return getMagicables1();
    }

    @Override
    List<Magicable> getMagicables3() {
        return Collections.singletonList(new Magician());
    }
}

1) T est utilisé lorsque vous voulez remplacer le nom par le nom réel tout en l'utilisant. Par exemple class MagicableWitch extends AbstractMagicable<Witch> .

Ici Witch a remplacé T et donc abstract List<T> getMagicables1(); est changé en List<Witch> getMagicables1() dans sa classe concrète.

2) ? est utilisé lorsque vous classe que la volonté est à remplacer sera disponible au moment de l'exécution.

3) List<Magicable> y List<Witch> sont différentes même si Witch implements Magicable . La mise en œuvre est présentée dans getMagicables3 .

1voto

Slimu Points 347

Dans votre premier cas, la méthode abstraite est déclarée comme utilisant un type générique <T extends Magicable> ce qui signifie que votre méthode peut retourner une liste de Magicable ou tout autre type qui l'implémente. Dans votre implémentation, vous retournez le type concret Magican qui est un Magicable. Vous pouvez ignorer l'avertissement et ajouter @SuppressWarning("unchecked") pour désactiver l'avertissement. Ce qu'il faut savoir, c'est que toutes les classes qui étendent votre classe ne pourront retourner que des listes de Magican.

Dans le second cas, la déclaration List<T> list = getMagicables(); jette une erreur parce que votre méthode ne renvoie pas un fichier List<T> mais un List<? extends Magicable' ce qui n'est pas la même chose. En raison de la façon dont les génériques fonctionnent, lorsque vous déclarez un type de retour qui utilise un caractère générique non lié, tout code qui appelle votre méthode doit avoir un type correspondant accepté, dans votre cas comme List<? extends Magicable> o List<?> .

En ce qui concerne le troisième cas, votre méthode abstraite renvoie un fichier List<Magicable> alors que votre implémentation renvoie un List<Magic> . Cela peut sembler contre-intuitif, mais vous ne pouvez pas faire quelque chose comme cela avec les génériques en Java : List<Magicable> list = ArrayList<Magic> . Cela peut sembler bizarre puisque les tableaux permettent de déclarer quelque chose comme Magicable[] magics = new Magican[3]; . Il s'agit d'une idée fausse courante, car les tableaux sont covariants alors que les génériques sont invariants. Ce que covariant signifie est que si vous avez deux classes Super y Sub extends Super , Sub[] is a subtype of Super[] . Pour les génériques, parce qu'ils sont invariants, il n'y a pas de relation entre ces deux éléments, un List<Sub> n'est pas un sous-type de List<Super> .

Si vous voulez retourner le type générique, il suffit d'utiliser la même déclaration de type que dans le premier cas protected <T extends Magicable> List<T> getMagicable() dans les classes qui étendent votre classe abstraite. C'est une très mauvaise idée d'utiliser des jokers dans le type retourné car vous obligez les utilisateurs de votre classe à utiliser des jokers dans leurs déclarations de variables List.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X