3 votes

Java Generics : casting to ? (ou un moyen d'utiliser un Foo<?> arbitraire)

J'ai donc un code qui ressemble à peu près à ceci (tronqué par souci de concision - ne pas tenir compte des variables membres publiques) :

  public class GenericThingy<T> {
    private T mValue;
    public final T[] mCandidates;

    public GenericThingy(T[] pCandidates, T pInitValue) {
      mCandidates = pCandidates;
      mValue = pInitValue;
    }

    public void setValue(T pNewValue) {
      mValue = pNewValue;
    }
  }

  public class GenericThingyWidget {

    private final GenericThingy<?> mThingy;
    private final JComboBox mBox;

    public GenericThingyWidget (GenericThingy<?> pThingy) {
      mThingy = pThingy;
      mBox = new JComboBox(pThingy.mCandidates);
      //do stuff here that makes the box show up
    }

    //this gets called by an external event
    public void applySelectedValue () {
      mThingy.setValue(mBox.getSelectedItem());
    }
  }
}

Mon problème est que l'appel mThingy.setValue(mBox.getSelectedItem()) ; génère l'erreur suivante :

La méthode setValue(capture#4-of ?) dans le type Generics.GenericThingy<capture#4-of ?> n'est pas applicable aux arguments (Objet)

Je peux contourner ce problème en supprimant le <?> de la déclaration de mThingy et pThingy dans GenericThingyWidget - ce qui me donne un "GenericThingy is a raw type. Les références à GenericThingy doivent être paramétrées".

J'ai également essayé de remplacer l'appel à setValue par

mThingy.setValue(mThingy.mCandidates[mBox.getSelectedIndex()]);

dont je m'attendais vraiment à ce qu'elle fonctionne, mais qui a produit une erreur très similaire :

La méthode setValue(capture#4-of ?) dans le type Generics.GenericThingy<capture#4-of ?> n'est pas applicable aux arguments (capture#5-of ?)

Existe-t-il un moyen de le faire ? sans en générant des avertissements "raw type" (je suis d'accord avec les avertissements "unchecked cast") et sans faire de GenericThingyWidget un type générique ? J'aurais pensé que je pouvais convertir le retour de mBox.getSelectedItem() en quelque chose, mais je n'arrive pas à trouver ce que cela pourrait être.

En prime, pourquoi l'appel de remplacement à mThingy.setValue ne fonctionne-t-il pas ?

3voto

GhiOm Points 13732

Vous manquez d'informations dans GenericThingyWidget . Les ? signifie : toute classe qui étend l'objet. Ce qui signifie tous no une personne en particulier, mais je ne sais pas laquelle . Java n'a pas de lien avec un ? à l'autre, elles ne peuvent pas être reliées l'une à l'autre dans une arborescence de classes. Ainsi, les

mThingy.setValue(mThingy.mCandidates[mBox.getSelectedIndex()]);

Ceci tente de placer un objet de tous dans la classe setValue, qui attend que la classe tous d'une autre classe, mais la ? ne peut pas dire Java ces deux tous devraient être de la même classe.

Sans paramétrage GenericThingyWidget Je ne vois aucun moyen de contourner ce problème.

Ce que je ferais : paramétrer GenericThingyWidget et créer une méthode statique paramétrée Factory :

public static <T> GenericThingyWidget<T> make(T someObject){
    ...
}

2voto

erickson Points 127945

Je vois deux possibilités.

Avec un ajout privé à GenericThingyWidget - Modèle d'aide à la capture de Goetz :

public void applySelectedValue() {
  helper(mThingy, mBox.getSelectedIndex());
}

private static <T> void helper(GenericThingy<T> pThingy, int pIndex) {
  pThingy.setValue(pThingy.mCandidates[pIndex]);
}

Ou bien, de manière rapide et pratique, en modifiant l'API de GenericThingy :

public void setValue(int value) {
  mValue = mCandidates[value];
}

En prime, pourquoi l'appel de remplacement à mThingy.setValue ne fonctionne-t-il pas ?

L'article de Brian Goetz l'explique probablement mieux que moi, mais je vais essayer.

mThingy.setValue(mThingy.mCandidates[mBox.getSelectedIndex()]);

Le compilateur sait que mThingy a un paramètre de type, mais il ne sait pas quel est ce type, car il s'agit d'un joker. Il crée un espace réservé pour ce type - "capture#4-of ?". Le compilateur sait également que mCandidates a un certain type, mais il ne sait pas non plus ce que c'est. Il crée un tout nouveau type de "capture" - "capture#5-de ?". Bien que vous et moi puissions penser que ces types devraient être les mêmes, le compilateur (du moins pour l'instant) ne peut pas tirer cette conclusion. C'est pourquoi vous obtenez le message d'erreur.

L'aide à la capture permet de contourner ce problème. Bien que le compilateur ne sache pas quel est le type, il sait qu'il a un type, et vous permet donc de le passer à la méthode d'aide. Une fois dans la méthode d'aide, il n'y a pas de caractères génériques, et le compilateur n'a pas à faire de raisonnement pour savoir si les caractères génériques se réfèrent réellement au même type.

1voto

Daniel Pryden Points 22167

Mise à jour

OK, essayez ceci :

  public class GenericThingy<T> {
    private Class<T> mClazz;
    private T mValue;
    public final T[] mCandidates;

    public GenericThingy(Class<T> clazz, T[] pCandidates, T pInitValue) {
      mClazz = clazz;
      mCandidates = pCandidates;
      mValue = pInitValue;
    }

    public void setValue(Object newValue) throws ClassCastException {
      mValue = mClazz.cast(newValue);
    }
  }

0voto

Yishai Points 42417

Ce qu'il faut faire, c'est paramétrer GenericThingyWidget comme suit :

public class GenericThingyWidget<T> {

private final GenericThingy<? super T> mThingy;
private final JComboBox mBox;

public GenericThingyWidget (GenericThingy<? super T> pThingy) {
  mThingy = pThingy;
  mBox = new JComboBox(pThingy.mCandidates);
  //do stuff here that makes the box show up
}

//this gets called by an external event
public void applySelectedValue () {
  mThingy.setValue((T) mBox.getSelectedItem());
 }
}
}

Techniquement, vous n'avez pas besoin du ? super T pour votre exemple, et vous seriez bien avec juste un T, et peut-être que ce serait mieux dans le code réel si vous vouliez obtenir du GenericThingy au lieu de juste l'insérer dedans.

0voto

Carl Points 4049

Comme l'a dit KLE, vous pouvez simplement dé-paramétrer GenericThingy (remplacer tous les T par des objets). En fait, je pense que vous devez le faire à moins que vous n'ayez l'intention de passer la classe de T au constructeur de GenericThingyWidget, et ensuite de faire un cast dynamique à partir de votre mbox.getSelectedItem(), puisque pour autant que je sache, getSelectedItem() ne renvoie que des Objects.

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