2015-01-13 3 views
0

두 개의 차량 인 Car 및 Train을 고려하십시오. Car의 생성자는 두 개의 인수를 받고 Train의 생성자는 하나의 인수를받습니다. 인수는 유형과 수량이 다릅니다. Java에서 varargs를 사용하여 두 생성자를 모두 호출 할 수있는 제네릭 메소드를 갖고 싶습니다. 차량, 자동차 및 기차를 변경할 수 없다고 가정합니다.가변 수량을 Java에서 해당 수량 및 유형으로 확장하십시오.

doSomething 메서드에서 args을 확장하는 방법에 문제가 있습니다. 변수 argsM 유형의 새 인스턴스 안에 던지면 작동하지 않습니다. 이게 가능합니까?

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

문제는 the newInstance method이 인수를 취하지 않는다는 것입니다. 존재하지 않는 경우 인수가없는 생성자를 호출합니다.

클래스가있을 때 생성자에 인수를 전달하려면 리플렉션을 사용하여 적절한 생성자를 찾아야합니다. Class 개체에서 the getConstructors() method을 호출하고 Constructor 개체의 반환 된 배열을 찾은 다음 적절한 개체를 찾으려면 the getConstructor method을 호출하고 매개 변수 유형을 나타내는 Class 개체를 전달하여 특정 생성자를 가져옵니다. 나열한 각 클래스에 대해 하나의 생성자 만 있으므로 getConstructors()에 의해 반환 된 배열의 첫 번째 요소를 가져옵니다.

Constructor이 있으면 newInstance method을 호출하면 해당 매개 변수에서 생성자 인수가 사용됩니다.

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

마지막 주를 :

은 열거 만들기 IMO 반사와 더 좋은 코드를

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

더 이상 필요.

+0

제 경우에는 질문에서 언급 한 것처럼 * 차량, 자동차 및 기차 *를 변경할 수 없습니다. 그래도 다른 경우에 유용합니다. – Daniel

+0

@Daniel 나는 수업을 전혀 변경하지 않았다 (글자 그대로 복사). 너는 필요 없어. – user2336315

+0

당신이 맞아요, 사과드립니다! 언급하지는 않았지만이 클래스를 수십 개의 클래스를 전달하는 프로젝트에 사용하고 리플렉션을 사용하면 훨씬 적은 코드가 포함됩니다. 나는 그것이 질문의 시점까지 더 느껴졌 기 때문에 이미 다른 대답을 선택했으나 감사합니다. (그것이 가치가 있었던 것에 따라 아직도 upvoted!) – Daniel