将可变参数扩展为相应的数量并输入 Java

Expanding varargs into corresponding quantity and type in Java

考虑两辆车,一辆汽车和一辆火车。 Car 的构造函数接收两个参数,而 Train 的构造函数接收一个参数。争论的类型和数量各不相同。我想要一个通用方法,可以在 Java 中使用可变参数调用两个构造函数。假设我无法更改 Vehicle、Car 和 Train。

问题是如何在方法doSomething中扩展args。将变量 args 放入类型 M 的新实例中是行不通的。这可行吗?

public class MainTest {

    // Receive concrete type of vehicle and arguments for its constructor (variable)
    public <M extends Vehicle> M doSomething(Class<M> a, Object... args)
            throws InstantiationException, IllegalAccessException {

        // How to pass variable number of arguments for the constructors?
        M m = a.newInstance(  args  );  // <-- Problem is here, possible to solve?

        return m;
    }

    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        MainTest test = new MainTest();
        Car c = test.doSomething(Car.class, "Jill", 35);
        Train t = test.doSomething(Train.class, 35.1);
    }
}

public class Vehicle {
    /* currently empty */
}

public class Car extends Vehicle {
    public Car(String name, int n) {
    }
}

public class Train extends Vehicle {
    public Train(double d) {
    }
}

问题是 the newInstance method 不接受任何参数。如果存在,它会调用无参数构造函数。

要在只有 class 时将参数传递给构造函数,您需要使用反射来找到合适的构造函数。在Class对象上,调用the getConstructors() method and loop over the returned array of Constructor objects to find the appropriate one, or call the getConstructor method,传入代表参数类型的Class对象,得到具体的构造函数。由于您列出的每个 class 只有一个构造函数,因此只需获取 getConstructors().

返回的数组的第一个元素

当您有合适的 Constructor 时,调用它的 newInstance method,它确实在其参数中接受构造函数参数。

粗略的例子...

public <M extends Vehicle> M doSomething(Class<M> a, Object... args)
        throws Exception {

    for(Constructor constructor : a.getConstructors()) {
      if(constructor.getParameterTypes().length == args.length) {
          return (M) constructor.newInstance(args);
      }
    }
    throw new Exception("constructor not found");
}

使用工厂设计模式可以更优雅地解决您的问题。

创建枚举:

enum VehicleType {CAR, TRAIN;}

那么你的工厂方法就变成了:

@SuppressWarnings("unchecked")
public static <T extends Vehicle> T buildVehicle(VehicleType model, Object... args) {
    switch (model) {
        case CAR:
            return (T) new Car((String) args[0], (int) args[1]);
        case TRAIN:
            return (T) new Train((double) args[0]);
        default:
            throw new IllegalArgumentException();
    }
}

最后是主要内容:

Car c = buildVehicle(VehicleType.CAR, "Jill", 35);
Train t = buildVehicle(VehicleType.TRAIN, 35.2);

不再需要反射和更好的代码 IMO。