2013-08-12 9 views
5

Tworzę klasę MethodPointer w celu symulacji funkcjonalności wskaźników funkcyjnych z C++. Na początku robiłem wszystko z zaledwie Object s, ale potem pomyślałem - dlaczego nie uczynić tego naprawdę generycznym?Dlaczego rzucanie klasy generycznej na klasę <T> jest niebezpieczne?

Problem był w tym konstruktora, który próbował wywołać inny konstruktor z podpisem MethodPointer(Class<T> clazz, String methodName, Class<?> ... paramClasses):

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

I zakładanego to będzie działać dobrze, ale otrzymałem następujący błąd kompilatora:

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

więc mylić, zrobiłem to:

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

To teraz kompiluje, ale otrzymuję następujące ostrzeżenie:

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

Myślę, że problemem jest to, że ja nie rozumiem, co Class<capture#1-of ? extends Object> środków. Myślałem, że ponieważ typ T jest wywnioskowany z parametru T object, że będzie konieczne, który wywołując object.getClass() zwraca obiekt Class typu Class. Najwyraźniej tak nie jest. Czy ktoś może wyjaśnić moje zamieszanie?


Pełna deklaracja klasy i wszyscy konstruktorzy:

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, jeśli jesteś otwarty do korzystania [Guava] (https://code.google.com/p/ guava-libraries /), sprawdź ich klasę ['Invokable'] (http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/reflect/Invokable.html). –

Odpowiedz

6

Odpowiedź SLaks wskazuje, że object.getClass() rozpada się na Class<? extends Object>, aby wyjaśnić błąd kompilacji. Ale jest to nie bezpieczne, aby przesłać do Class<T>.

getClass "zwraca klasę środowiska wykonawczego" obiektu, do którego jest wywoływana. Na przykład, jeśli jesteśmy wewnątrz MethodPointer<Number>, następnie objectis-aNumber ale jej typ Runtime może być Integer, Double, itd. To nam mówi, że rzucając object.getClass() do Class<T> nie jest bezpieczne, ponieważ Class<Integer> i Class<Number> są różne obiekty , reprezentujących różne klasy - rozróżnienie, które wydaje się bardzo istotne dla poprawności tego, co próbujesz zrobić.

Jakie jest rozwiązanie? Cóż, nie rzucaj. Nalegać, aby od dzwoniącego odebrać Class<T>.

+1

+1 OP może uzyskać niespójne wyniki w zależności od tego, który konstruktor jest wywoływany, jeśli 'T' jest typem nieostatnim. – Muel

+0

Czy użycie "Klasa " wszędzie pomoże? (inne niż dawanie dziwnych wyników przy wskazywaniu na metody "końcowe" w cieniu) – SLaks

+0

@SLaks Na pewno pomogłoby - rzucenie na to byłoby bezpieczne. Naprawdę zapomniałem, że 'getClass' nie zwróci' Class 'jeśli spojrzysz na moją historię edycji :) Ponieważ OP za pomocą' getMethod', który również przeszukuje nadklasy, użycie 'Class ' wydaje się w porządku - ale jeśli chce użyj 'getDeclaredMethod' (aby znaleźć metody niepubliczne), będzie potrzebował dokładnie' Class '. –

4

Problemem jest to, że getClass() ignoruje parametry typu.

Javadocs powiedzieć:

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.

Na przykład new ArrayList<String>().getClass() zwraca Class<? extends ArrayList> nie Class<? extends ArrayList<String>>.

Oznacza to również, że parametr getClass() zanikł do parametru Class<? extends Object>.

+1

Czy downcast nie byłby bezpieczny z powodu parametru type "T" określonego w pierwszym parametrze do konstruktora 'MethodPointer'? –

+0

@KevinBowersox: Tak, jest bezpieczny, ale typ 'getClass()' w czasie kompilacji specyficznie ignoruje parametry typu. – SLaks

+3

Niebezpieczny nie zawsze oznacza, no cóż, niebezpieczny. Wszystko to tak naprawdę oznacza, że ​​kompilator (jako ograniczony przez JLS) nie może potwierdzić bezpieczeństwa typu. – yshavit

0

Jeśli nie potrzebują określonego typu Class, można użyć

Class<?> 

dla Ciebie parametrów.

Masz więc niezwiązany typ dla swoich zajęć.

A jeśli chcesz uzyskać instancję można go używać tak:

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