Dynamicznie Utwórz obiekt w Javie z nazwy klasy i ustaw pola klasy za pomocą listy z danymi

Mam listę zawierającą dane typu String -> ["classField1", "classField2", "classField3"]

Mam metodę (myMethod(List list, String className)), która akceptuje jako parametr listę. Mogę więc przekazać tę listę przez parametr do myMethod (List list, String className).

W myMethod chcę utworzyć jeden obiekt, który będzie instancją nazwy klasy, czyli drugim parametrem. Następnie chcę ustawić pola klasy za pomocą danych z listy. Ze względu na fakt, że chcę dynamicznie uzyskiwać pola z class, wynikiem powyższego jest to, że muszę oddać każdą wartość String listy, do typu każdego pola klasy.

Jestem pewien, że kolejność łańcuchów wewnątrz listy jest we właściwej kolejności i odpowiada Polom klasy o tej samej kolejności.

Czy ktoś ma jakiś pomysł jak wykonać powyższe?

Przykład:

["StringtempValue", "StringUnitOfMeasurement"] =>

Utwórz obiekt instancji:

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 z następujących sposób:

Właściwie chcę stworzyć obiekt z istniejącej klasy i starałem się 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 dostaję wyjątek od 2. linii, kiedy próbuje wytworzyć nowy obiekt. Jak powiedział @JB Nijet nie wiedziałem, że metoda getDeclaredFields () nie zwraca posortowanych pól.

Właściwie, mam metodę, która akceptuje tylko List of string, więc używając reflection konwertuję obiekt na List of string, a potem chcę zrób odwrotnie. Nie myślałem o innym sposobie.

Author: Georgios Bouloukakis, 2012-12-14

3 answers

Dynamiczne tworzenie instancji obiektów może być dość złożone, a twój scenariusz dotyka kilku aspektów:

  • Konwersja wartości obiektu z String na odpowiedni typ
  • Ładowanie właściwej klasy z nazwy klasy i tworzenie instancji
  • przypisanie tych wartości do obiektu
[15]}dokładne omówienie każdego z tych punktów zajmowałoby cały rozdział w bez wątpienia interesującym traktowaniu Javy jako języka dynamicznego. Ale, zakładając, że nie masz czasu na poznanie tych zawiłości lub uzależnienie od jakiejś ogromnej biblioteki stron trzecich, stwórzmy coś, co zabierze cię na Twoją drogę. Proszę trzymać ręce w pojeździe przez cały czas, ponieważ jazda będzie wyboista.

Zajmijmy się najpierw kwestią konwersji typów. Wartości są dostarczane jako Strings, ale obiekt będzie je przechowywać jako double, long, int, itd. Więc potrzebujemy funkcji, która parsuje String do odpowiedniego celu "type": "content"]}

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 typy wewnętrzne. Ale nie szukamy tu doskonałości, prawda? Więc proszę poprawić odpowiednio. Zauważ, że konwersja z String na inny typ jest w rzeczywistości formą deserializacji, więc nakładasz ograniczenia na swoich klientów (ktokolwiek daje ci Strings), aby dostarczyć ich wartości w określonych formatach. W tym przypadku formaty są definiowane przez zachowanie metod parse. Ćwiczenie 1: w pewnym momencie w w przyszłości zmień format w sposób niezgodny Wstecz, aby wywołać czyjś gniew.

Teraz zróbmy rzeczywistą instancję:

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);
}
Idziemy na skróty, ale to nie jest Kaplica Sykstyńska, którą tworzymy. Wystarczy załadować klasę i wyszukać konstruktor, którego liczba parametrów odpowiada liczbie argumentów (np. arity). Przeciążeni konstruktorzy o tej samej arytmetyce? Nie, Nie zadziała. Varargs? Nie, Nie zadziała. Konstruktorzy niepubliczni? Nie, Nie zadziała. A jeśli nie możesz zagwarantować, że twoja klasa dostarczy konstruktor, który ustawia wszystkie pola tak, jak robi to twój przykład TempStruct, to kończę z tym dniem i napiję się piwa, ponieważ takie podejście jest DOA.

Gdy znajdziemy konstruktor, wykonaj pętlę nad argami String, Aby przekonwertować je na typy oczekiwane przez konstruktor. Zakładając, że to działa, wywołujemy konstruktora poprzez odbicie, machamy magiczną różdżką i mówimy Abrakadabra. Voilà: masz nowy obiekt.

Let ' s spróbuj z bardzo wymyślnym przykładem:

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ście:

TempStruct 373.15 Kelvin

GLORIOUS

 40
Author: cambecc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-07 07:07:21

Kiedyś miałem ten sam problem i hashMap okazał się dla mnie rozwiązaniem.

Zobacz też: http://docs.oracle.com/javase/6/docs/api/java/util/HashMap.html

 2
Author: TRU7H,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-13 21:49:59

Spójrz na http://commons.apache.org/beanutils / Pakiet. Umożliwia dostęp do pól po nazwie.

 0
Author: Stepan Yakovenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-13 22:59:44