2013-08-12 11 views
5

Sto creando una classe MethodPointer per simulare la funzionalità dei puntatori di funzione da C++. All'inizio, stavo facendo tutto con solo Object s, ma poi ho pensato: perché non renderlo veramente generico?Perché la classe di un generico in Classe non è sicura?

Il problema è venuto in questo costruttore, che ha tentato di chiamare un altro costruttore con la firma MethodPointer(Class<T> clazz, String methodName, Class<?> ... paramClasses):

public MethodPointer(T object, String methodName, Class<?> ... paramClasses) { 
    this(object.getClass(), methodName, paramClasses); 
    this.object = object; 
} 

ho assunto questo avrebbe funzionato bene, ma ho ricevuto il seguente errore del compilatore:

The constructor MethodPointer<T>(Class<capture#1-of ? extends Object>, 
String, Class<?>[]) is undefined 

Così, confuso, ho fatto questo:

public MethodPointer(T object, String methodName, Class<?> ... paramClasses) { 
    this((Class<T>) object.getClass(), methodName, paramClasses); 
    this.object = object; 
} 

Ora compila, ma ricevo il seguente avviso:

Unchecked cast from Class<capture#1-of ? extends Object> to Class<T> 

Credo che il problema è che non capisco cosa Class<capture#1-of ? extends Object> mezzi. Ho pensato che dal momento che il tipo di T object deduce dal parametro T object che sarebbe necessario che chiamando object.getClass() restituisce un oggetto Class di tipo Class<T>. Apparentemente questo non è il caso, però. Qualcuno può chiarire la mia confusione?


dichiarazione di classe completa e tutti i costruttori:

public class MethodPointer<T> { 

    //Logger instance 
    private static final Logger LOGGER = Logger.getLogger(MethodPointer.class); 

    //Object fields 
    private final Method method; 
    private ArrayList<Object> args = new ArrayList<Object>(); 
    private T object = null; 


    //Constructors 
    public MethodPointer(Method method) { 
     this.method = method; 
    } 

    public MethodPointer(Class<T> clazz, String methodName, Class<?> ... paramClasses) { 
     Method theMethod = null; 
     try { 
      theMethod = clazz.getMethod(methodName, paramClasses); 
     } 
     catch(NoSuchMethodException nsme) { 
      LogUtil.log(LOGGER, Level.ERROR, "Unable to find method " + methodName + " in " + clazz.getSimpleName(), nsme); 
     } 
     method = theMethod; 
    } 

    public MethodPointer(T object, String methodName, Class<?> ... paramClasses) { 
     this((Class<T>) object.getClass(), methodName, paramClasses); 
     this.object = object; 
    } 
+0

Btw, se siete aperti a utilizzare [Guava] (https://code.google.com/p/ guava-libraries /), controlla la loro classe ['Invokable'] (http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/reflect/Invokable.html). –

risposta

6

La risposta di SLaks indica che object.getClass() decade a Class<? extends Object>, per spiegare l'errore di compilazione. Ma è non sicuro per trasmettere a Class<T>.

getClass "restituisce la classe di runtime" dell'oggetto su cui è chiamato. Per esempio, se siamo all'interno di un MethodPointer<Number>, poi objectis-aNumber ma il suo tipo di esecuzione potrebbe essere Integer, Double, ecc Questo ci dice che la fusione object.getClass()-Class<T> non è sicuro perché Class<Integer> e Class<Number> sono diversi oggetti , che rappresentano diverse classi - una distinzione che sembra molto rilevante per la correttezza di ciò che stai cercando di fare.

Quindi qual è la soluzione? Bene, non lanciare. Insistere per prendere un Class<T> dal chiamante.

+1

+1 L'OP può ottenere risultati incoerenti a seconda del costruttore chiamato, se 'T' è un tipo non finale. – Muel

+0

Userebbe 'Classe ' ovunque aiuto? (oltre a dare strani risultati quando si punta a metodi 'finale' in ombra) – SLaks

+0

@SLaks Sarebbe sicuramente d'aiuto: lanciare su quello sarebbe sicuro. In realtà ho dimenticato 'getClass' non restituire' Classe 'se si guarda la mia cronologia delle modifiche :) Poiché l'OP sta usando' getMethod', che cerca anche superclassi, usando un 'Classe ' sembra buono - ma se vuole usa 'getDeclaredMethod' (per trovare metodi non pubblici), avrà esattamente bisogno di' Classe '. –

4

Il problema è che getClass() ignora parametri di tipo.

Il Javadocs dicono:

The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called.

Per esempio, new ArrayList<String>().getClass() restituisce una Class<? extends ArrayList>, non Class<? extends ArrayList<String>>.

Ciò significa anche che getClass() chiamato su un parametro di tipo decade a Class<? extends Object>.

+1

Il downcast non sarebbe sicuro se il parametro di tipo 'T' fosse specificato nel primo parametro nel costruttore' MethodPointer'? –

+0

@KevinBowersox: Sì, è sicuro, ma il tipo 'compile-time 'di getClass()' ignora specificamente i parametri di tipo. – SLaks

+3

Non sicuro non sempre significa, bene, non sicuro. Tutto ciò che significa veramente è che il compilatore (come vincolato dal JLS) non può _confirm_ il tipo safety. – yshavit

0

Se non avete bisogno di una classe-tipo specificato, è possibile utilizzare

Class<?> 

per voi i parametri.

Quindi si dispone di un tipo non associato per le classi.

E se si vuole ottenere un'istanza si potrebbe usare in questo modo:

obj.getClass().getDeclaredConstructor(Class<?> parameterTypes) 
Problemi correlati