Java: Instanceof e Generics

Prima di esaminare la mia struttura di dati generici per l’indice di un valore, vorrei vedere se è anche un’istanza del tipo a cui è stato parametrizzato.

Ma Eclipse si lamenta quando faccio questo:

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

Questo è il messaggio di errore:

Imansible eseguire l’instanceof check con il parametro type E. Utilizzare invece il suo object di cancellazione poiché le informazioni di tipo generico verranno cancellate in fase di runtime

Qual è il modo migliore per farlo?

    Il messaggio di errore dice tutto. A runtime, il tipo è sparito, non c’è modo di verificarlo.

    Potresti prenderlo creando una fabbrica per il tuo object in questo modo:

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

    E poi nell’archivio costruttore dell’object che digita, così variabile in modo che il tuo metodo possa assomigliare a questo:

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

    Due opzioni per il controllo del tipo di runtime con generici:

    Opzione 1: danneggia il tuo costruttore

    Supponiamo che tu stia sovrascrivendo indexOf (…), e vuoi controllare il tipo solo per le prestazioni, per salvarti ripetendo l’intera collezione.

    Costruisci un costruttore schifoso come questo:

     public MyCollection(Class t) { this.t = t; } 

    Quindi puoi utilizzare isAssignableFrom per verificare il tipo.

     public int indexOf(Object o) { if ( o != null && !t.isAssignableFrom(o.getClass()) ) return -1; //... 

    Ogni volta che istanziate il vostro object, dovreste ripetervi:

     new MyCollection(Apples.class); 

    Potresti decidere che non ne vale la pena. Nell’implementazione di ArrayList.indexOf (…) , non controllano che il tipo corrisponda.

    Opzione 2: lascia fallire

    Se hai bisogno di usare un metodo astratto che richiede il tuo tipo sconosciuto, allora tutto ciò che vuoi veramente è che il compilatore smetta di piangere su instanceof . Se hai un metodo come questo:

     protected abstract void abstractMethod(T element); 

    Puoi usarlo in questo modo:

     public int indexOf(Object o) { try { abstractMethod((T) o); } catch (ClassCastException e) { //... 

    Stai lanciando l’object a T (il tuo tipo generico), solo per ingannare il compilatore. Il tuo cast non esegue nulla in fase di esecuzione , ma otterrai comunque una ClassCastException quando proverai a passare il tipo di object sbagliato nel tuo metodo astratto.

    NOTA 1: se stai facendo cast aggiuntivi non controllati nel tuo metodo astratto, le tue ClassCastException verranno catturate qui. Potrebbe essere buono o cattivo, quindi pensaci.

    NOTA 2: ottieni un assegno nullo gratuito quando usi instanceof . Dal momento che non è ansible utilizzarlo, potrebbe essere necessario verificare la presenza di null a mani nude.

    Se la tua class estende una class con un parametro generico, puoi anche ottenerla in fase di esecuzione tramite reflection, e quindi usarla per il confronto, cioè

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

    Nel caso precedente, in fase di esecuzione si otterrà String.class da getParameterizedClass () e si memorizzerà nella cache in modo da non ottenere alcun overhead di riflessione su più controlli. Si noti che è ansible ottenere gli altri tipi parametrizzati per indice dal metodo ParameterizedType.getActualTypeArguments ().

    Vecchio post, ma un modo semplice per eseguire un’istanza generica.

     public static  boolean isInstanceOf(Class clazz, Class targetClass) { return clazz.isInstance(targetClass); } 

    Ho avuto lo stesso problema ed ecco la mia soluzione (molto umile, @george: questa volta compilando AND lavorando …).

    Il mio probem era all’interno di una class astratta che implementa Observer. L’aggiornamento del metodo di esecuzione Observable (…) con la class Object che può essere qualsiasi tipo di object.

    Voglio solo gestire gli oggetti di tipo T

    La soluzione è passare la class al costruttore per poter confrontare i tipi in fase di esecuzione.

     public abstract class AbstractOne implements Observer { private Class tClass; public AbstractOne(Class 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); } 

    Per l’implementazione dobbiamo solo passare la class al costruttore

     public class OneImpl extends AbstractOne { public OneImpl() { super(Rule.class); } @Override public void foo(Rule t){ } } 

    Oppure potresti prendere un tentativo fallito di lanciare in E eg.

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

    Tecnicamente non dovresti farlo, questo è il punto di generici, quindi puoi fare il controllo del tipo compilato:

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

    ma la @Override potrebbe essere un problema se si dispone di una gerarchia di classi. Altrimenti vedi la risposta di Yishai.

    Il tipo di runtime dell’object è una condizione relativamente arbitraria da filtrare. Ti suggerisco di tenere lontana la tua pigrizia dalla tua collezione. Ciò si ottiene semplicemente facendo debind la raccolta a un filtro passato in una costruzione.

     public interface FilterObject { boolean isAllowed(Object obj); } public class FilterOptimizedList implements List { 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 longStrs = new FilterOptimizedList( 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; } }} );