2013-04-17 6 views
6

Ciao a tutti :) Sto provando a scegliere il costruttore giusto in una classe. Ecco il codice:Controllo al runtime se una classe ha un costruttore specifico che sta usando i generici

Constructor[] constructors = targetClass.getConstructors(); 
Constructor goodConstructor = null; 
for (Constructor constructor : constructors) { 
    Class[] parameterTypes = constructor.getParameterTypes(); 
    if (parameterTypes.length = 1 && parameterTypes[0].equals(Map.class)) {//here 
     goodConstructor = constructor; 
    } 
} 

voglio cambiare Map.class-Map<String, String>.class. Ricordo vagamente che i generici sono solo per la compilazione, quindi questo è il motivo per cui il compilatore si lamenta. Come posso verificare in runtime che la classe abbia il costruttore giusto?

migliori saluti

risposta

8

che si desidera utilizzare getGenericParameterTypes() invece:

public class FindConstructor { 

    public static void main(String[] args) throws IOException { 
     for (Constructor<?> constructor : MyClass.class.getConstructors()) { 
      Type[] parameterTypes = constructor.getGenericParameterTypes(); 
      if (parameterTypes.length == 1 && parameterTypes[0] instanceof ParameterizedType) { 
       ParameterizedType parameterizedArg = (ParameterizedType) parameterTypes[0]; 
       if (parameterizedArg.getRawType() != Map.class) { 
        continue; 
       } 

       if (parameterizedArg.getActualTypeArguments()[0] != String.class) { 
        continue; 
       } 

       if (parameterizedArg.getActualTypeArguments()[1] != String.class) { 
        continue; 
       } 
      } 
      System.out.println("found constructor " + constructor); 
     } 
    } 
} 

class MyClass { 
    public MyClass(Map<String, String> map) { 
    } 
} 

Ora, se si cambia MyClass() prendere un Map<String, Integer> lo farà partita non è più.

Questo diventa molto più semplice con Guava TypeToken, che utilizza una classe anonima per creare un parametro Type che possiamo confrontare.

Type mapStringString = new TypeToken<Map<String, String>>(){}.getType(); 
for (Constructor<?> constructor : MyClass.class.getConstructors()) { 
    Type[] parameterTypes = constructor.getGenericParameterTypes(); 
    if (parameterTypes.length == 1 && parameterTypes[0].equals(mapStringString)) { 
     System.out.println("found constructor " + constructor); 
    } 
} 
+0

Rimosso la mia risposta; Dovrei stare più attento prima di rispondere con la risposta "generics use type erasure"! Direi che se stai facendo questo, però, c'è sicuramente qualcosa di sbagliato nel tuo design! :) – James

0

classi non funzionano in questo modo in java. Le classi non possono essere parametrizzate in fase di esecuzione; solo istanze. Esiste solo uno Map.class, anziché (come sembra aver ipotizzato) una classe separata per ogni implementazione generica di Map. Il seguente codice può aiutare a chiarire questo:

Map<String,String> m1 = new HashMap<>(); 
    Map<Object,Long> m2 = new HashMap<>(); 

    System.out.println(m1.getClass() == m2.getClass());//prints "true" 
+0

Anche se questo è vero per 'Class', Java ha il supporto per rappresentare un tipo che ha parametri di tipo, ovvero' ParameterizedType' (vedi la mia risposta). I parametri di tipo sono abbastanza spesso compilati in (ogni volta che si dichiara un campo o parametro generico, per i principianti). Se tali informazioni non fossero disponibili nel file di classe, non si sarebbe in grado di compilare una classe che utilizza i generici. –