[Это сообщение было отредактировано, чтобы включить упрощенную версию копирования / вставки основной проблемы.]
Я работаю над проектом 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