在实用程序类中反映方法并在Java中使用varargs调用它们

在实用程序类中反映方法并在Java中使用varargs调用它们,java,reflection,static-methods,variadic-functions,utility-method,Java,Reflection,Static Methods,Variadic Functions,Utility Method,我用Java构建了一个非常基本的实用程序类来处理数据库操作(连接检索、插入等),如下所示: // define the package name package com.foo.bar.helpers; // import all needed resources import com.foo.bar.helpers.database.MySQL; import com.foo.bar.helpers.database.SQLite; import java.lang.reflect.Meth

我用Java构建了一个非常基本的实用程序类来处理数据库操作(连接检索、插入等),如下所示:

// define the package name
package com.foo.bar.helpers;

// import all needed resources
import com.foo.bar.helpers.database.MySQL;
import com.foo.bar.helpers.database.SQLite;
import java.lang.reflect.Method;
import java.sql.Array;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;

/**
 * database
 * @author John Doe <...>
 */
class Database {
    // private constructor to prevent instantiation
    private Database() throws InstantiationException {
        // throw the appropriate exception
        throw new InstantiationException();
    }

    // database classes
    public static final String SQLITE_CLASS = SQLite.class.getCanonicalName();
    public static final String MYSQL_CLASS = MySQL.class.getCanonicalName();

    /**
     * returns a connection to the database using a set of parameters
     * @param parameters the connection parameters
     * @return a connection to the database
     * @author John Doe <...>
     */
    public static Connection getConnection(Object... parameters) {
        Connection output = null;

        try {
            if (parameters.length > 0) {
                // create an instance of the target class
                Class<?> target_class = Class.forName(parameters[0].getClass().getCanonicalName());

                // remove the first parameter (database class)
                Object[] class_parameters = Arrays.copyOfRange(parameters, 1, parameters.length);

                // retrieve the class type for each parameter
                Class<?>[] class_types = new Class[class_parameters.length];

                for (int i = 0; i < class_parameters.length; i++) {
                    class_types[i] = class_parameters[i].getClass();
                }

                // reflect the target class method
                Method class_method = target_class.getDeclaredMethod("getConnection", class_types);

                // output the database connection
                output = (Connection) class_method.invoke(null, class_parameters);
            } else {
                throw new Throwable("unable to establish a connection with the database (no parameters were provided)");
            }
        } catch (Throwable e) {
            // print the stack trace
            e.printStackTrace();
        }

        return output;
    }
}
我明白了:

java.lang.NoSuchMethodException: java.lang.String.getConnection(java.lang.String, java.lang.Integer, java.lang.String, java.lang.String, java.lang.String)
    at java.base/java.lang.Class.getDeclaredMethod(Class.java:2475)
    at com.foo.bar.helpers.Database.getConnection(Database.java:146)
    at com.foo.xxxxxxxx.Application.main(Application.java:61)
如果我没有正确阅读文档,我需要得到我想要反映的类的一个实例,得到我想与
getDeclaredMethod
一起使用的特定方法(因为我的任何实用程序类中的每个方法都是静态的),方法名为
String
,参数数量可变(或者一个数组,如果我正确使用的话)的类类型

要做到这一点,我需要调用传递
null
的方法作为第一个参数(因为它是一个静态方法,静态方法不需要我试图调用特定方法的类的实例)和一个可变数量的参数(与前面相同),参数本身也是如此

我从
e.printStackTrace()
得到的错误告诉我方法获取失败,或者是因为我没有正确指定类类型(我很怀疑是否使用
class[]
而不是
class[]
,但是IntelliJ抱怨
原始使用参数化类“class”
)或者我没有真正得到我想从中得到实例的类的实例,而是得到了某种类型的泛型类对象(因此我无法真正看到我正在寻找的方法)

或者可能是因为我声明了一个私有构造函数来避免实例化(但在阅读了一些文章之后,我认为实用程序类(如果您真的需要使用它们)应该有一个私有构造函数来避免实例化…因此才声明了私有构造函数),但是,不管怎样,我现在有点搞砸了:(

这个想法是能够连接到任何给定的数据库(因为,现在,它只是MySQL和SQLite,但将来可能是Amazon Redshift、BigQuery、PostgreSQL、Oracle等),但我可能对通用访问的想法是错误的


你能给我一个提示吗?

你给出的异常提示你试图在类
java.lang.String
中找到方法
getConnection()
。我怀疑你没有把它放在那里,所以它什么也找不到

数据库#getConnection
类中,我注意到以下语句

Class<?> target_class = Class.forName(parameters[0].getClass().getCanonicalName());
传递所需类类型的实例,如

Database.getConnection(new MySQL() ,  ... ); // defenitly not recommended, only really useable if an instance itself is needed (e.g. Non-static access) 

// in the #getConnection class
Class<?> target_class = parameters[0].getClass() // get the class-type instance
Database.getConnection(new MySQL(),…);//不推荐使用,仅在需要实例本身时才真正可用(例如非静态访问)
//在#getConnection类中
Class target\u Class=参数[0]。getClass()//获取类类型实例
传递所需类类型的字符串表示形式(规范名称)

Database.getConnection(MtSQL.DB_CLASS_NAME,  ... ); // pass String type argument

// in the #getConnection class
Class<?> target_class = Class.forName(parameters[0]) // the #forName needs a String argument, so we can pass it directly.
Database.getConnection(MtSQL.DB_CLASS_NAME,…)//传递字符串类型参数
//在#getConnection类中
Class target_Class=Class.forName(参数[0])/#forName需要一个字符串参数,因此我们可以直接传递它。
在最新的示例中,您可能会喜欢使用
类加载器
等。它提供了很好的功能,如缓存和类卸载。但它相当复杂,因此可能不是您第一次使用的


最后,作为一般建议,Java是强类型的,具有方法重载等特性为了您自己的理智起见,尽可能多地使用它。上述3种情况很容易过载,从而使参数验证成为一项不那么痛苦的任务。它也使API用户使用它“万无一失”,并且在编译过程中会注意到类型错配。

在您的
公共静态连接getConnection中(字符串主机、int端口、字符串架构、字符串用户名、字符串密码)
方法,能否将
int-port
更改为
Integer-port
?我很好奇您是否仍然遇到相同的问题exception@JacobG.当然可以。我将if从
int
更改为
Integer
,但我得到了相同的例外问题是
数据库。MYSQL\u类是一个
字符串,您使用的是
类target_class=class.forName(参数[0].getClass().getCanonicalName());
。但是,
target_class
将是
String.class
而不是
MySQL.class
(因为
参数[0].getClass()
返回
String.class
)@JacobG。即使我选择了正确的第一个响应,我也欠你一个大响应。端口必须是整数而不是int。否则,它再次失败,所以…非常感谢你的提示:)我尝试了第三个解决方案,它成功了。
Class target\u Class=Class.forName(参数[0].toString())
是我工作所需要的一切。你真的救了我一天,朋友:D
Database.getConnection(MySQL.class,  ... );

// in the #getConnection class
Class<?> target_class = parameters[0] // type is already a class, so just assign
Database.getConnection(new MySQL() ,  ... ); // defenitly not recommended, only really useable if an instance itself is needed (e.g. Non-static access) 

// in the #getConnection class
Class<?> target_class = parameters[0].getClass() // get the class-type instance
Database.getConnection(MtSQL.DB_CLASS_NAME,  ... ); // pass String type argument

// in the #getConnection class
Class<?> target_class = Class.forName(parameters[0]) // the #forName needs a String argument, so we can pass it directly.