Java: Instanceof et Generics

avant de passer en revue ma structure de données génériques pour un indice de valeur, j'aimerais voir si c'est même une instance du type this qui a été paramétrée.

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:

ne peut pas effectuer instanceof check par rapport au paramètre de type E. utilisez plutôt son objet erasure car les informations de type générique seront effacées à runtime

Quelle est la meilleure façon de le faire?

109
demandé sur Nathan Kleyn 2009-10-15 07:00:03

8 réponses

Le message d'erreur dit tout. A 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 puis dans le constructeur de l'objet stocker ce type, donc variable de sorte que votre méthode pourrait ressembler à ceci:

        if (arg0 != null && !(this.type.isAssignableFrom(arg0.getClass()))
        {
            return -1;
        }
70
répondu Yishai 2009-10-15 13:22:09

deux options pour la vérification de type runtime avec generics:

Option 1-corrompre votre constructeur

supposons que vous dépassez l'indexOf (...), et vous voulez vérifier le type juste pour la performance, pour vous sauver itérer l'ensemble de la collection.

faire un constructeur sale comme ceci:

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

    this.t = t;
}

ensuite, vous pouvez utiliser isassignable from 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 ça n'en vaut pas la peine. Dans la mise en œuvre de ArrayList.indexOf(...) , ils ne vérifient pas que le type correspond.

Option 2 - ne parviennent pas Laisser

si vous devez utiliser une méthode abstraite qui nécessite votre type inconnu, alors tout ce que vous voulez vraiment c'est que le compilateur arrête de pleurer sur instanceof . Si vous avez une méthode comme ceci:

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 lancez l'objet à T (votre type générique), juste pour tromper le compilateur. votre plâtre ne fait rien à l'exécution , mais vous obtiendrez toujours une ClassCastException quand vous essayez de passer le mauvais type d'objet dans votre méthode abstraite.

NOTE 1: Si vous faites des moulages supplémentaires non contrôlés dans votre méthode abstraite, vos ClassCastExceptions seront attrapées ici. Ça pourrait être bien ou mal, alors réfléchis bien.

NOTE 2: vous obtenez un contrôle null gratuit lorsque vous utilisez instanceof . Puisque vous ne pouvez pas l'utiliser, vous pouvez avoir besoin de vérifier null à mains nues.

34
répondu SharkAlley 2017-05-23 12:10:30

pourvu que votre classe étende une classe avec un paramètre générique, vous pouvez aussi obtenir cela à l'exécution via la réflexion, et ensuite utiliser cela pour la comparaison, i.e.

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, à l'exécution, vous obtiendrez String.classe de getParameterizedClass(), et il met en cache afin de ne pas obtenir de toute réflexion généraux sur de multiples contrôles. Notez que vous pouvez obtenir les autres types paramétrés par index à partir du ParameterizedType.méthode getactualttypearguments ().

12
répondu terryscotttaylor 2011-07-09 00:10:51

ancien post, mais une façon simple de faire générique instanceOf check.

public static <T> boolean isInstanceOf(Class<T> clazz, Class<T> targetClass) {
    return clazz.isInstance(targetClass);
}
11
répondu Jonas Pedersen 2015-02-18 10:04:33

j'ai eu le même problème et voici ma solution (très humble, @george: cette fois compiler et travailler ...).

mon probem était dans une classe abstraite qui met en œuvre L'Observateur. La mise à jour de la méthode Observable fires(...) avec une classe D'objets qui peut être n'importe quel type d'objet.

je ne veux gestionnaire d'Objets de type T

La solution est de passer la classe du constructeur afin de pouvoir comparer les types d' Runtime.

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 la mise en œuvre nous avons juste à passer la classe au constructeur

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

  @Override
  public void foo(Rule t){
  }
}
7
répondu hsaturn 2014-11-16 00:11:06

Ou vous pourriez attraper l'échec d'une tentative de jeter dans E par exemple.

public int indexOf(Object arg0){
  try{
    E test=(E)arg0;
    return doStuff(test);
  }catch(ClassCastException e){
    return -1;
  }
}
5
répondu ben 2012-10-28 05:55:28

techniquement, vous ne devriez pas avoir à, c'est le point de génériques, donc vous pouvez faire le contrôle de type de 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
répondu Jason S 2009-10-15 03:13:25

le type d'exécution de l'objet est une condition relativement arbitraire à filtrer. Je suggère de garder une telle pudeur loin de votre collection. Cela est simplement obtenu en ayant votre délégué de collecte à un filtre passé 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
répondu Tom Hawtin - tackline 2009-10-15 04:08:29