2016-02-24 11 views
6

Sto utilizzando un contenitore eterogeneo simile a this one. Posso mettere e ricevere oggetti dal contenitore con facilità:Iterating over eterogeneous container

Favorites f = new Favorites(); 
f.putFavorite(String.class, "Java"); 
String someString = f.getFavorite(String.class); 

Ma ci sembra essere un modo semplice per iterare su tale contenitore. Posso aggiungere un metodo keySet() alla classe Favorites e semplicemente restituire il set di chiavi del Map oggetto interno:

public Set<Class<?>> keySet() { 
    return favorites.keySet(); 
} 

Ora, vorrei scandire le chiavi, utilizzare i tasti per ottenere i valori associati, e chiamare alcuni metodi sugli oggetti ricevuti:

for (Class<?> klass : f.keySet()) { 
    // f.getFavorite(klass).<SOME_METHOD_SPECIFIC_TO_THE_CLASS-KEY> 
} 

ho pensato che avrei potuto accedere ai metodi degli oggetti tenuti nel mio contenitore chiamando klass.cast(f.getFavorite(klass)).SOME_METHOD(), ma non funziona neanche (significato, non posso accedere a qualsiasi metodo, ad eccezione per i metodi basati su Object).

Diciamo che nel mio caso di utilizzo vorrei esaminare le interfacce di tutti questi oggetti che ho ripetuto e agire di conseguenza nell'interfaccia rilevata. Supponiamo anche che io possa avere dozzine di oggetti di varie classi e che tutti implementino una delle tre interfacce.

L'unica soluzione che posso pensare è di riempire il mio codice con decine di controlli isinstance, ma preferirei un approccio meno ingombrante (ovvero controllare se un dato oggetto implementa una delle tre interfacce).

+0

Avete un idea dell '"approccio meno ingombrante" che stai immaginando? Voglio dire se hai tre diversi pezzi di codice che vuoi eseguire a seconda che implementi una delle tre diverse interfacce, sembra che qualsiasi approccio che possiamo suggerire dovrà avere tre rami diversi. Quindi sarebbe bello avere chiarezza su ciò che stai trovando proprio macchinoso. –

+0

Scusa, forse non ero abbastanza chiaro. Considero il "controllare se un dato oggetto implementa una delle tre interfacce" approccio per essere meno ingombrante. Almeno se confrontato con lo scenario con dozzine di affermazioni "se". –

+0

Non sono sicuro di dove andremo da 3 se le dichiarazioni a dozzine ... la mia risposta userebbe 3 se le affermazioni, è quello che stai cercando? Il problema con il controllo di tre interfacce in una sola volta è che hai bisogno di fare qualcosa di diverso a seconda di quale delle tre interfacce corrisponde comunque, quindi cosa ti guadagna? –

risposta

5

Provando a chiamare un metodo specifico su ogni voce, si sta sostanzialmente dicendo che si conosce meglio del compilatore e che si sa che ogni voce ha una super classe specifica.

Se si sa che è il caso, è possibile utilizzare Class#asSubclass di digitare klass come Class<? extends KnownSuper> in modo che getFavorite sarà poi tornare una sottoclasse di KnownSuper (e quindi esporre il metodo):

Class<KnownSuper> superClass = KnownSuper.class; //class with callMethod() 
for (Class<?> klass : f.keySet()) { 
    f.getFavorite(klass.asSubClass(superClass)).callMethod() 
} 

Tuttavia, questo sarà ovviamente dare un'eccezione di runtime se una delle classi chiave non si estende KnownSuper. Quindi, se quanto sopra sarebbe sicuro, dovresti parametrizzare il tuo contenitore eterogeneo per accettare solo le classi chiave che si estendono da KnownSuper in primo luogo.

Se non tutte le voci sarà essere di questo tipo, si potrebbe anche verificare prima se la chiave è indicato quando l'iterazione:

Class<KnownSuper> superClass = KnownSuper.class; //class with callMethod() 
for (Class<?> klass : f.keySet()) { 
    if (superClass.isAssignableFrom(klass)) { 
     f.getFavorite(klass.asSubClass(superClass)).callMethod() 
    } 
} 
0

Basta passare attraverso questo semplice esempio

Diciamo che avete sotto Preferiti definizione di classe

public class Favorites extends HashMap<String, String> { 

} 

Ecco la classe di test

public class TestGeneric { 

    public static void main(String[] args) { 

     Favorites f = new Favorites(); 
     f.put("test", "test"); 

     for (String test1 : f.keySet()) { 

      f.get("").charAt(index)// you will see all string specific method as compiler knows in advance what object map going to contain 


    } 

} 

Nel momento in cui si cambia Favorites extends HashMap<String, String> a Favorites extends HashMap sarà solo obiettare metodi specifici come compilatore non sa in anticipo quale oggetto Preferiti sta per mettere in mappa

+1

Il punto di utilizzo del contenitore ** eterogeneo ** deve essere in grado di inserire ** oggetti eterogenei **. Ad esempio: 'f.putFavorite (String.class," Java "); f.putFavorite (Integer.class, 1); '. –