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.
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
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
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
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.
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