java - 在反射中,方法对象的参数类与参数类不匹配?

标签 java class reflection

[这篇文章已被编辑以包含基本问题的简化复制/粘贴版本。]

我正在开发一个 Reflection 项目,该项目将具有一些类似于 JUnit 的功能,但我遇到了一个障碍,该程序似乎感觉我有同一类的 2 个不同版本。

我编写了一个简单的 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 似乎无法找到与我的请求相匹配的崩溃方法版本。 crash 方法应该接收一个 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 类。知道可能是什么问题吗?我检查了两个类对象(objectClass 和 paramClass),它们看起来是相同的,甚至到类加载器中的路径也是如此。

除了 isAssignableFrom(),我还尝试了 isInstance,但它也不起作用:

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

最佳答案

问题来自于每次查找 Class 对象时创建一个新的 URLClassLoader 对象。通过仅使用 1 个 URLClassLoader 作为静态变量,问题就得到了解决。

关于java - 在反射中,方法对象的参数类与参数类不匹配?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60123810/

相关文章:

java - 在 JPA 中为非规范化表定义一个实体(充当聚合器)

c# - 如何在声明时使用其父实例实例化属性

c++ - Qt 基类函数定义

c++ - 在类语法帮助中比较对象;

c# - 获取通用的自定义属性

java - Reactive Mongo - DeleteAllBy...查询 - 找不到类型类 java.lang.Void 的 PersistentEntity

java - 加载、链接和初始化——什么时候加载一个类?

java - 从单独的 jar 调用 .asSubclass 时抛出 ClassCastException

c# - 如何使用反射在 IEnumerable<T> 上调用 System.Linq.Enumerable.Count<>?

java - lucene中 boolean 逻辑的正确使用