2009-10-15 19 views
101

Avant de regarder dans ma structure de données générique pour l'index d'une valeur, j'aimerais voir si c'est même une instance du type this qui a été paramétrée.Java: Instanceof et Generics

Mais Eclipse se plaint quand je fais ceci:

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

Voici le message d'erreur:

Impossible d'effectuer instanceof vérifier contre paramètre de type E. Utilisez plutôt son objet d'effacement étant donné que les informations de type générique être effacé à l'exécution

Quelle est la meilleure façon de le faire?

Répondre

67

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

Vous pouvez l'attraper en faisant une usine pour votre objet comme celui-ci:

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

Et puis dans le magasin que le type de constructeur de l'objet, si variable afin que votre méthode pourrait ressembler à ceci:

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

Je ne pense pas que vous voulez 'Class.isAssignableFrom'. –

+0

@Tom, j'ai écrit cette nuit dernière de mémoire, et je l'ai réparé pour effectivement passer la classe (duh!) Mais sinon, je ne comprends pas pourquoi vous ne le voudriez pas (peut-être que j'ai besoin de plus de café ce matin, suis seulement sur ma première tasse). – Yishai

+0

Je suis avec Tom. Pourriez-vous clarifier cela? Utiliser isAssignableFrom() aurait été mon choix pour le travail. Peut-être qu'il me manque quelque chose? –

1

techniquement, vous ne devriez pas avoir à, c'est le point de génériques, de sorte que vous pouvez faire la vérification de type compilation:

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

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

+0

Oui, l'interface List exige que la fonction prenne un paramètre d'objet. –

+0

vous implémentez List? Pourquoi n'appliquez-vous pas la liste ? –

+0

(voir par exemple la déclaration de ArrayList: http://java.sun.com/j2se/1.5.0/docs/api/java/util/ArrayList.html) –

1

Le type d'exécution de l'objet est une condition relativement arbitraire à filtrer. Je suggère de garder une telle muckiness loin de votre collection. Ceci est simplement réalisé en faisant déléguer votre collection à 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; 
      } 
     }} 
    ); 
+0

(Bien que vous vouliez faire un type d'instance, vérifiez si vous utilisez 'Comparator' ou similaire.) –

12

à condition que votre classe étend une classe avec un paramètre générique, vous pouvez également obtenir ce lors de l'exécution par la réflexion, et ensuite utiliser que pour la comparaison, à savoir

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 à partir de getParameterizedClass(), et il mettra en cache de sorte que vous n'obtenez aucun frais de réflexion lors de plusieurs contrôles. Notez que vous pouvez obtenir les autres types paramétrés par index à partir de la méthode ParameterizedType.getActualTypeArguments().

4

Vous pourriez également avoir raté une tentative de conversion en E , par exemple.

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

+1 Une solution pragmatique non sur-modélisée pour une vérification simple! J'aurais aimé pouvoir voter plus – higuaro

+22

Cela ne fonctionnerait pas. E est effacé lors de l'exécution, donc la distribution n'échouera pas, vous obtiendrez juste un avertissement du compilateur à ce sujet. – Yishai

30

Deux options pour le type d'exécution de vérification avec les génériques:

Option 1 - Corrupt votre constructeur

Supposons que vous surchargez indexOf (...), et que vous voulez vérifiez le type juste pour la performance, pour vous épargner itérer toute la collection.

Faire un constructeur crasseux comme ceci:

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 instancier votre objet que vous auriez à vous répéter:

new MyCollection<Apples>(Apples.class); 

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

Option 2 - Laissez échouer

Si vous avez besoin d'utiliser une méthode abstraite qui nécessite votre type inconnu, tout ce que vous voulez vraiment est pour le compilateur pour arrêter de pleurer sur les 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 caster l'objet à T (votre type générique), juste pour tromper le compilateur. Your cast does nothing at runtime, mais vous obtiendrez toujours une exception ClassCastException lorsque vous essayez de transmettre le mauvais type d'objet dans votre méthode abstraite.

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

NOTE 2: You get a free null check when you use instanceof. Puisque vous ne pouvez pas l'utiliser, vous devrez peut-être vérifier null avec vos mains nues.

7

J'ai eu le même problème et voici ma solution (très humble, @george: cette fois compilant ET travaillant ...). Mon problème était à l'intérieur d'une classe abstraite qui implémente Observer. La méthode Observable met à jour la méthode (...) avec la classe Object qui peut être n'importe quel type d'objet.

Je veux seulement handler objets de type T

La solution est de passer la classe au constructeur afin de pouvoir comparer les types à 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 la mise en œuvre, nous devons juste passer la classe au constructeur

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

    @Override 
    public void foo(Rule t){ 
    } 
} 
9

, mais un moyen simple de faire la vérification de instanceOf générique.

public static <T> boolean isInstanceOf(Class<T> clazz, Class<T> targetClass) { 
    return clazz.isInstance(targetClass); 
} 
+3

On ne sait pas vraiment ce que vous contribuez réellement ici. – Andrew