在Java中将varargs扩展为相应的数量和类型

时间:2015-01-13 21:03:57

标签: java generics variadic-functions

考虑两辆车,一辆汽车和一辆火车。 Car的构造函数接收两个参数,Train的构造函数接收一个参数。参数在类型和数量上都不同。我想要一个可以在Java中使用varargs调用两个构造函数的泛型方法。假设我不能改变车辆,汽车和火车。

问题在于如何在方法args中展开doSomething。在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) {
    }
}

3 个答案:

答案 0 :(得分:3)

问题是the newInstance method没有任何参数。它调用无参数构造函数(如果存在)。

要在只有类时将参数传递给构造函数,您需要使用反射来查找适当的构造函数。在Class对象上,调用the getConstructors() method并遍历返回的Constructor个对象数组以查找相应的对象,或调用the getConstructor method,传递Class个对象表示获取特定构造函数的参数类型。由于您列出的每个类只有一个构造函数,因此只需获取getConstructors()返回的数组的第一个元素。

当你拥有适当的Constructor时,调用它的newInstance method,它确实在其参数中包含构造函数参数。

答案 1 :(得分:1)

原油示例......

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");
}

答案 2 :(得分:1)

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

创建一个枚举:

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。