web-dev-qa-db-fra.com

Java: instance et génériques

Avant de rechercher dans ma structure de données générique un index de valeur, j'aimerais savoir si c'est même une instance du type auquel this a été paramétré.

Mais Eclipse se plaint quand je fais ceci:

@Override
public int indexOf(Object arg0) {
    if (!(arg0 instanceof E)) {
        return -1;
    }

C'est le message d'erreur:

Impossible d'effectuer une instance de vérification avec le paramètre de type E. Utilisez plutôt son objet d'effacement car les informations de type génériques seront effacées lors de l'exécution

Quelle est la meilleure façon de le faire?

121
Nick Heiner

Le message d'erreur dit tout. Au moment de l'exécution, le type est parti, il n'y a aucun moyen de le vérifier.

Vous pourriez l'attraper en faisant une usine pour votre objet comme ceci:

 public static <T> MyObject<T> createMyObject(Class<T> type) {
    return new MyObject<T>(type);
 }

Et ensuite, dans le constructeur du magasin de l'objet, stockez ce type, si variable que votre méthode pourrait ressembler à ceci:

        if (arg0 != null && !(this.type.isAssignableFrom(arg0.getClass()))
        {
            return -1;
        }
74
Yishai

Deux options pour la vérification du type d’exécution avec les génériques:

Option 1 - Corrigez votre constructeur

Supposons que vous substituez indexOf (...) et que vous souhaitez vérifier le type uniquement pour obtenir des performances optimales, afin de vous éviter de parcourir la collection dans son intégralité.

Faire un constructeur sale comme ça:

public MyCollection<T>(Class<T> t) {

    this.t = t;
}

Ensuite, vous pouvez utiliser isAssignableFrom pour vérifier le type.

public int indexOf(Object o) {

    if (
        o != null &&

        !t.isAssignableFrom(o.getClass())

    ) return -1;

//...

Chaque fois que vous instanciez votre objet, vous devez vous répéter:

new MyCollection<Apples>(Apples.class);

Vous pourriez décider que cela n'en vaut pas la peine. Dans l'implémentation de ArrayList.indexOf (...) , ils ne vérifient pas que le type correspond.

Option 2 - Laissez-le échouer

Si vous devez utiliser une méthode abstraite nécessitant votre type inconnu, vous souhaitez uniquement que le compilateur arrête de pleurer sur instanceof . Si vous avez une méthode comme celle-ci:

protected abstract void abstractMethod(T element);

Vous pouvez l'utiliser comme ceci:

public int indexOf(Object o) {

    try {

        abstractMethod((T) o);

    } catch (ClassCastException e) {

//...

Vous transmettez l'objet à T (votre type générique), juste pour tromper le compilateur. Votre distribution ne fait rien au moment de l'exécution , mais vous obtiendrez quand même une exception ClassCastException lorsque vous essayez de transmettre le type d'objet incorrect à votre méthode abstraite.

REMARQUE 1: Si vous effectuez des conversions supplémentaires non contrôlées dans votre méthode abstraite, vos exceptions ClassCastExceptions seront capturées ici. Cela peut être bon ou mauvais, alors réfléchissez bien.

NOTE 2: Vous obtenez un contrôle nul gratuit lorsque vous utilisez instanceof . Comme vous ne pouvez pas l'utiliser, vous devrez peut-être vérifier la valeur null à mains nues.

37
SharkAlley

Old post, mais un moyen simple de faire une vérification générique instanceOf.

public static <T> boolean isInstanceOf(Class<T> clazz, Class<T> targetClass) {
    return clazz.isInstance(targetClass);
}
14
Jonas Pedersen

Si votre classe étend une classe avec un paramètre générique, vous pouvez également l'obtenir au moment de l'exécution via une réflexion, puis l'utiliser à des fins de comparaison, c'est-à-dire.

class YourClass extends SomeOtherClass<String>
{

   private Class<?> clazz;

   public Class<?> getParameterizedClass()
   {
      if(clazz == null)
      {
         ParameterizedType pt = (ParameterizedType)this.getClass().getGenericSuperclass();
          clazz = (Class<?>)pt.getActualTypeArguments()[0];
       }
       return clazz;
    }
}

Dans le cas ci-dessus, lors de l'exécution, vous obtiendrez String.class de getParameterizedClass (), qui se mettra en cache afin d'éviter toute réflexion supplémentaire lors de plusieurs vérifications. Notez que vous pouvez obtenir les autres types paramétrés par index à l'aide de la méthode ParameterizedType.getActualTypeArguments ().

12
terryscotttaylor

J'ai eu le même problème et voici ma solution (très humble, @george: cette fois, la compilation ET le travail ...).

Mon probem était à l'intérieur d'une classe abstraite qui implémente Observer. La méthode de mise à jour Observable (...) avec la classe Object pouvant être tout type d’Objet.

Je veux seulement manipuler des objets de type T

La solution consiste à transmettre la classe au constructeur afin de pouvoir comparer les types au moment de l'exécution.

public abstract class AbstractOne<T> implements Observer {

  private Class<T> tClass;
    public AbstractOne(Class<T> clazz) {
    tClass = clazz;
  }

  @Override
  public void update(Observable o, Object arg) {
    if (tClass.isInstance(arg)) {
      // Here I am, arg has the type T
      foo((T) arg);
    }
  }

  public abstract foo(T t);

}

Pour l'implémentation, il suffit de passer la classe au constructeur

public class OneImpl extends AbstractOne<Rule> {
  public OneImpl() {
    super(Rule.class);
  }

  @Override
  public void foo(Rule t){
  }
}
7
hsaturn

Ou vous pourriez attraper une tentative infructueuse de lancer dans E par exemple.

public int indexOf(Object arg0){
  try{
    E test=(E)arg0;
    return doStuff(test);
  }catch(ClassCastException e){
    return -1;
  }
}
5
ben

Techniquement, vous n’auriez pas à le faire, c’est le point des génériques. Vous pouvez donc effectuer une vérification de type compilation:

public int indexOf(E arg0) {
   ...
}

mais alors le @Override peut être un problème si vous avez une hiérarchie de classe. Sinon, voir la réponse de Yishai.

1
Jason S

Le type d'exécution de l'objet est une condition relativement arbitraire sur laquelle filtrer. Je suggère de garder une telle saleté loin de votre collection. Ceci est simplement obtenu en faisant passer votre délégué de collection à un filtre dans une construction.

public interface FilterObject {
     boolean isAllowed(Object obj);
}

public class FilterOptimizedList<E> implements List<E> {
     private final FilterObject filter;
     ...
     public FilterOptimizedList(FilterObject filter) {
         if (filter == null) {
             throw NullPointerException();
         }
         this.filter = filter;
     }
     ...
     public int indexOf(Object obj) {
         if (!filter.isAllows(obj)) {
              return -1;
         }
         ...
     }
     ...
}

     final List<String> longStrs = new FilterOptimizedList<String>(
         new FilterObject() { public boolean isAllowed(Object obj) {
             if (obj == null) {
                 return true;
             } else if (obj instanceof String) {
                 String str = (String)str;
                 return str.length() > = 4;
             } else {
                 return false;
             }
         }}
     );
1