В Reflection класс параметров объекта метода не соответствует классу аргумента? - PullRequest
0 голосов
/ 08 февраля 2020

[Это сообщение было отредактировано, чтобы включить упрощенную версию копирования / вставки основной проблемы.]

Я работаю над проектом Reflection, у которого будет некоторая функциональность, аналогичная JUnit, но я натолкнувшись на препятствие, когда программа, кажется, чувствует, что у меня есть две разные версии одного и того же класса.

Я написал простой класс Car следующим образом.

public class Car {
    private String name;

    public Car(String n) {
        name = n;
        System.out.println(name + " was constructed.");
    }

    public void honk() {
        System.out.println("beep beep");
    }

    public void crash(Car other) {
        System.out.println(name + " crashes into " + other.name);
    }
}

Я могу успешно проверить функциональность автомобиля такова:

public class CarRunner {
    public static void main(String[] args) {
        Car a = new Car("Model T");
        Car b = new Car("Tesla");
        a.honk();                 //prints "beep beep" 
        a.crash(b);               //prints "Model T crashes into Tesla"
    }
}

Все вышеперечисленное прекрасно работает.

Теперь я хочу воспроизвести результаты CarRuner, но с некоторыми методами тестирования функциональности, которые я написал используя отражение. Используя рефлексию, я могу запросить создание объекта и вызов методов с этими объектами. Он прекрасно работает до последнего теста, когда в качестве аргумента используется пользовательский класс.

import java.io.*;
import java.lang.invoke.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.*;
import java.net.*;

public class TesterTool {    
    //Where are the class files that I am testing?
    private static File classPath = new File("C:\\Users\\Spatter\\Desktop\\Autograder\\SimpleCarDemo"); 

    public static Object makeObject(String nameOfClass, Object[] arguments) {
        Object retObject = null; //goal is to get an object in here of the requested class.
        try {      
            //What type of object are we trying to construct?
            URL classUrl = classPath.toURI().toURL();
            URLClassLoader classLoader = URLClassLoader.newInstance(new URL[]{classUrl});
            Class<?> c = Class.forName(nameOfClass, true, classLoader);           

            //What kind of arguments do we have?
            Class[] argumentTypes = new Class[arguments.length];            
            for (int i = 0; i < arguments.length; i++) {
                argumentTypes[i] = arguments[i].getClass();
            }

            //Lets find a constructor that can accept the type of arguments we have
            Constructor con = c.getConstructor(argumentTypes);                          

            FutureTask<?>  theTask = new FutureTask<Object>(new Callable<Object>() 
                    {
                        public Object call() {
                            Object retObject = null;                            
                            try {                                    
                                retObject = con.newInstance(arguments);
                            } catch (Exception e) { return e; }
                            return retObject;
                        }
                    });

            ExecutorService es = Executors.newSingleThreadExecutor();
            es.execute(theTask);
            retObject = theTask.get(3, TimeUnit.SECONDS);
            es.shutdownNow();
            if (retObject instanceof Exception) throw new Exception();                         

        } catch (Exception e) {
            System.out.print("Error: Unable to construct object" + e);                   
        }
        return retObject;
    }

    public static Object testMethod(Object invokingObject, String methodName, Object[] arguments) {
        Object retObject = null; //if the method we test returns an object, we will do the same.
        try {   

            //What type of object are we trying to construct?           
            Class c = invokingObject.getClass(); 

            //Alternate version of getting class type using ClassLoader         
            //Class originalc = invokingObject.getClass();     
            //String nameOfClass = originalc.getName();            
            //URL classUrl = classPath.toURI().toURL();
            //URLClassLoader classLoader = URLClassLoader.newInstance(new URL[]{classUrl});
            //Class<?> c = Class.forName(nameOfClass, true, classLoader); 

            //What kind of arguments do we have?
            Class[] argumentTypes = new Class[arguments.length];            
            for (int i = 0; i < arguments.length; i++) {
                argumentTypes[i] = arguments[i].getClass();
            }

            //Lets find a method that can accept the type of arguments we have
            Method m = c.getMethod(methodName, argumentTypes);                          

            FutureTask<?>  theTask = new FutureTask<Object>(new Callable<Object>() 
                    {
                        public Object call() {
                            Object retObject = null;                            
                            try {                                    
                                retObject = m.invoke(invokingObject, arguments);
                            } catch (Exception e) { return e; }
                            return retObject;
                        }
                    });

            ExecutorService es = Executors.newSingleThreadExecutor();
            es.execute(theTask);
            retObject = theTask.get(3, TimeUnit.SECONDS);
            es.shutdownNow();
            if (retObject instanceof Exception) throw new Exception();                         

        } catch (Exception e) {
            System.out.print("Error: Unable to run method " + e);                   
        }
        return retObject;
    }

    public static void main(String[] args) {
        //Find the Car class and invoke the constructor that receives a String parameter.
        Object o1 = makeObject("Car", new Object[]{"Model T"});      //this works fine.
        Object o2 = makeObject("Car", new Object[]{"Tesla"});        //this works fine.

        //Invoke the honk method of object o1.  No parameters required.
        //The result is that "beep beep" is printed.
        testMethod(o1, "honk", new Object[] {});                     //this works fine.

        //Invoke the crash(Car c) method of o1 using o2 as the parameter.
        //This should print "Model T crashes into Tesla".
        testMethod(o1, "crash", new Object[] {o2});          //this doesn't work.
    }
}

В этом последнем тесте моя проблема вступает в игру. testMethod, похоже, не может найти версию метода cra sh, которая соответствует моему запросу. Предполагается, что метод cra sh получает объект Car, что и делает, но, похоже, он недостаточно хорош.

Я также попробовал очень сложную альтернативную версию этого, где я получаю все методы класса Car и пытаюсь найти тот, который соответствует сигнатуре, но кажется, что объект класса Car не не объект класса автомобиля. (См. Ниже.)

Class objectClass  = o2.getClass();
Class[] paramTypes = method.getParameterTypes(); //where method is the Method object for crash
Class paramClass = paramTypes[0]; //there was only 1 paramType.  I confirmed that it's the Car class.
System.out.println(objectClass); //prints class Car
System.out.println(paramClass); //prints class Car
if (paramClass.isAssignableFrom(objectClass)) {      //always returns false?
    System.out.println("I want to run this method because the signature matches.");
    // o1 should invoke method using FutureTask
}

isAssignableFrom () всегда возвращает false, даже если они оба являются классами Car. Есть идеи, в чем может быть проблема? Я проверил оба объекта Class (objectClass и paramClass), и они кажутся идентичными, даже вплоть до путей в ClassLoaders.

Вместо isAssignableFrom (), я также попробовал isInstance, но это тоже не сработало:

if (paramClass.isInstance(o2)) {      //also always returns false

1 Ответ

0 голосов
/ 08 февраля 2020

Проблема возникла из-за создания нового объекта URLClassLoader каждый раз, когда я находил объект Class. При наличии только 1 URLClassLoader в качестве переменной stati c проблема решена.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...