2012-12-13 13 views
10

Mam listę zawierającą dane z typu String ->["classField1", "classField2", "classField3"]Dynamicznie utworzyć obiekt w Java z nazwy klasy i ustawienie pól klasy za pomocą listy z danymi

Mam metoda (myMethod(List list, String className)), która przyjmuje jako parametr Lista. Tak, mogę przekazać tę listę za pośrednictwem parametru do myMethod (lista list, String className).

W myMethod, chcę utworzyć jeden obiekt, który będzie instancją klasy, który jest drugim parametrem. Potem chcę ustawić pola klasy, używając danych z Listy. Z uwagi na fakt, że chcę uzyskać dynamicznie pola klasy, wynik powyższego jest taki, że muszę rzutować każdą wartość ciągu z listy na typ każdego pola klasy.

Jestem pewna, że ​​kolejność napisów znajdujących się wewnątrz listy jest poprawna i odpowiada polom klasy o tej samej kolejności.

Czy ktoś ma pojęcie, jak wykonać powyższe czynności?

Przykład:

["StringtempValue", "StringUnitOfMeasurement"] =>

Tworzenie instancji obiektu:

public class TempStruct { 

    private double tempValue; 
    private String unitOfMeasurement; 

    public TempStruct(double tempValue, String unitOfMeasurement) { 
    this.tempValue = tempValue; 
    this.unitOfMeasurement = unitOfMeasurement; 
    } 

} 

staram się dać rozwiązanie następujący sposób:

Właściwie Chcę stworzyć obiekt istniejącej klasy i próbowałem to zrobić z refleksją. Używam następującego kodu:

Class<?> cls = Class.forName(name); 
Object clsInstance = (Object) cls.newInstance(); 
Field[] objectFields = clsInstance.getClass().getDeclaredFields(); 

Ale otrzymuję wyjątek od drugiej linii, gdy próbuje utworzyć nowy obiekt. Jako, że @JB Nijet powiedział, że nie wiedziałem, że metoda getDeclaredFields() nie zwraca pól posortowanych.

Właściwie, mam metodę, która akceptuje tylko listę ciągów, więc za pomocą refleksji konwertuję obiekt na listę ciągów, a następnie chcę zrobić odwrotnie. Nie myślałem, że w inny sposób to zrobić.

+2

http://docs.oracle.com/javase/tutorial/reflect/index .html Z czym konkretnie masz problemy? –

+0

Obie instrukcje getFields() i getDeclaredFields() mówią: "Elementy zwróconej tablicy nie są sortowane i nie są w żadnej określonej kolejności".Zastanawiam się, na jakim rodzaju pól polegasz. –

+1

Nie jestem pewien, czy chcesz utworzyć i wypełnić obiekt istniejącej klasy (w takim przypadku, jak zasugerował @PeterLawrey, potrzebujesz refleksji), lub jeśli chcesz dynamicznie tworzyć i kompilować nową klasę i zwracać puste wystąpienie tej klasy. W przypadku drugiego wymagania należy zapoznać się z [javax.tools.JavaCompiler API] (http://docs.oracle.com/javase/7/docs/api/javax/tools/JavaCompiler.html) lub biblioteką manipulacji Bytecode, np. jako [ASM] (http://asm.ow2.org/) lub [BCEL] (http://commons.apache.org/bcel/). –

Odpowiedz

39

Dynamiczny instancji obiektów można uzyskać bardzo skomplikowane, a scenariusz dotyka kilku aspektach:

  • konwersji wartości obiektu od String do odpowiedniego typu
  • załadowaniem właściwej klasy z nazwą klasy i tworzenie wystąpienie
  • przypisanie tych wartości do obiektu

dokładne omówienie każdego z tych punktów zajęłoby cały rozdział w bez wątpienia nitującym traktowaniu Javy jako dynamicznego języka. Ale zakładając, że nie masz czasu, aby nauczyć się tych zawiłości, lub uzależnić się od jakiejś ogromnej biblioteki osób trzecich, zróbmy coś, co cię poruszy. Trzymaj ręce wewnątrz pojazdu przez cały czas, ponieważ jazda będzie wyboista.

Zajmijmy się najpierw kwestią konwersji typu. Wartości są dostarczane jako Strings, ale Twój obiekt będzie przechowywać je jako double, long, int itp Więc musimy funkcję, która analizuje String do odpowiedniego typu docelowa:

static Object convert(Class<?> target, String s) { 
    if (target == Object.class || target == String.class || s == null) { 
     return s; 
    } 
    if (target == Character.class || target == char.class) { 
     return s.charAt(0); 
    } 
    if (target == Byte.class || target == byte.class) { 
     return Byte.parseByte(s); 
    } 
    if (target == Short.class || target == short.class) { 
     return Short.parseShort(s); 
    } 
    if (target == Integer.class || target == int.class) { 
     return Integer.parseInt(s); 
    } 
    if (target == Long.class || target == long.class) { 
     return Long.parseLong(s); 
    } 
    if (target == Float.class || target == float.class) { 
     return Float.parseFloat(s); 
    } 
    if (target == Double.class || target == double.class) { 
     return Double.parseDouble(s); 
    } 
    if (target == Boolean.class || target == boolean.class) { 
     return Boolean.parseBoolean(s); 
    } 
    throw new IllegalArgumentException("Don't know how to convert to " + target); 
} 

Ugh. To jest brzydkie i obsługuje tylko wewnętrzne typy. Ale nie szukamy tu doskonałości, prawda? Więc proszę odpowiednio ulepszyć. Zauważ, że konwersja z String do innego typu jest w rzeczywistości formą deserializacji, a więc umieszczasz ograniczenia na swoich klientach (ktokolwiek poda ci Strings) w celu podania ich wartości w określonych formatach. W tym przypadku formaty są definiowane przez zachowanie metod parse. Ćwiczenie 1: W pewnym momencie w przyszłości zmień format w sposób niekompatybilny wstecz, aby spowodować czyjś gniew.

Teraz zróbmy rzeczywistą konkretyzacji:

static Object instantiate(List<String> args, String className) throws Exception { 
    // Load the class. 
    Class<?> clazz = Class.forName(className); 

    // Search for an "appropriate" constructor. 
    for (Constructor<?> ctor : clazz.getConstructors()) { 
     Class<?>[] paramTypes = ctor.getParameterTypes(); 

     // If the arity matches, let's use it. 
     if (args.size() == paramTypes.length) { 

      // Convert the String arguments into the parameters' types. 
      Object[] convertedArgs = new Object[args.size()]; 
      for (int i = 0; i < convertedArgs.length; i++) { 
       convertedArgs[i] = convert(paramTypes[i], args.get(i)); 
      } 

      // Instantiate the object with the converted arguments. 
      return ctor.newInstance(convertedArgs); 
     } 
    } 

    throw new IllegalArgumentException("Don't know how to instantiate " + className); 
} 

Zabieramy dużo skrótów tutaj, ale hej, to nie jest Kaplica Sykstyńska tworzymy. Po prostu wczytaj klasę i wyszukaj konstruktor, którego liczba parametrów jest zgodna z liczbą argumentów (np. Arity). Przeciążone konstruktory tej samej arii? Nie, nie zadziała. Varargs? Nie, nie zadziała. Niepublicznych konstruktorów? Nie, nie zadziała. A jeśli nie możesz zagwarantować, że twoja klasa dostarczy konstruktora, który ustawi wszystkie pola, jak na przykład Twój przykład: TempStruct, to nazwałbym to dzień i łapię piwo, ponieważ to podejście jest DOA.

Po znalezieniu konstruktora przeprowadź pętlę nad argumentami String, aby przekonwertować je na typy oczekiwane przez konstruktor. Zakładając, że działa, wywołujemy konstruktora poprzez odbicie, machamy magiczną różdżką i mówimy abrakadabra. Voila: masz nowy obiekt.

Spróbujmy go z bardzo contrived przykład:

public static void main(String[] args) throws Exception { 
    TempStruct ts = 
     (TempStruct)instantiate(
      Arrays.asList("373.15", "Kelvin"), 
      TempStruct.class.getName()); 

    System.out.println(
     ts.getClass().getSimpleName() + " " + 
     ts.tempValue + " " + 
     ts.unitOfMeasurement); 
} 

wyjściowa:

TempStruct 373.15 Kelvin 

GLORIOUS

+0

Mam błąd wypróbowania tej funkcji, musimy wymienić argumenty: convert (args.get (i), paramTypes [i]); – mfruizs2

Powiązane problemy