it-swarm.com.de

Erstellen Sie in Java dynamisch ein Objekt aus einem Klassennamen und setzen Sie Klassenfelder mithilfe einer Liste mit Daten

Ich habe eine Liste, die Daten mit dem Zeichenfolgentyp enthält -> ["classField1", "classField2", "classField3"]

Ich habe eine Methode (myMethod(List list, String className)), die die Liste als Parameter akzeptiert. Ich kann diese Liste also über den Parameter an myMethod übergeben (List list, String className). 

In myMethod möchte ich ein Objekt erstellen, bei dem es sich um eine Instanz des Klassennamens handelt, dh um den zweiten Parameter. Danach möchte ich die Felder der Klasse mithilfe der Daten der Liste festlegen. Aufgrund der Tatsache, dass ich die Felder der Klasse dynamisch erhalten möchte, ist das Ergebnis der obigen Ausführungen, dass ich jeden String-Wert der Liste in den Typ jedes Felds der Klasse umwandeln muss.

Ich bin sicher, dass die Reihenfolge der Zeichenfolgen in der Liste in der richtigen Reihenfolge liegt und den Feldern der Klasse mit derselben Reihenfolge entspricht.

Hat jemand eine Idee, wie das oben ausgeführt werden soll?

Beispiel:

["StringtempValue", "StringUnitOfMeasurement"] =>

Instanzobjekt erstellen:

public class TempStruct {

   private double tempValue;
   private String unitOfMeasurement;

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

}

Ich versuche eine Lösung auf folgende Weise zu geben:

Eigentlich möchte ich ein Objekt aus einer vorhandenen Klasse erstellen und habe es mit Reflektion versucht. Ich verwende den folgenden Code:

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

Ich bekomme jedoch eine Ausnahme in der zweiten Zeile, wenn versucht wird, das neue Objekt zu erstellen. Wie NJet sagte, wusste ich nicht, dass die Methode getDeclaredFields () die sortierten Felder nicht zurückgibt.

Eigentlich habe ich eine Methode, die nur List of Strings akzeptiert. Mit Reflektion konvertiere ich das Objekt in String-Liste. Danach möchte ich das Gegenteil tun. Ich dachte nicht an einen anderen Weg, es zu tun. 

13

Die dynamische Instantiierung von Objekten kann ziemlich komplex werden, und Ihr Szenario berührt mehrere Aspekte:

  • konvertieren der Objektwerte von String in den entsprechenden Typ
  • laden der rechten Klasse aus dem Klassennamen und Erstellen einer Instanz
  • diese Werte dem Objekt zuweisen

Eine eingehende Erörterung jedes dieser Punkte würde ein ganzes Kapitel in einer zweifellos faszinierenden Behandlung von Java als einer dynamischen Sprache in Anspruch nehmen. Vorausgesetzt, Sie haben nicht die Zeit, diese Feinheiten zu erlernen oder von einer riesigen Bibliothek von Drittanbietern abhängig zu sein, lassen Sie uns etwas aufspüren, das Sie auf den Weg bringt. Bitte halten Sie Ihre Hände immer im Fahrzeug, da die Fahrt holprig wird.

Lassen Sie uns zuerst das Problem der Typkonvertierung angehen. Die Werte werden als Strings angegeben, aber Ihr Objekt speichert sie als double, long, int usw. So benötigen wir eine Funktion, die eine String in den entsprechenden Zieltyp parst:

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

Pfui. Dies ist hässlich und behandelt nur intrinsische Typen. Aber wir suchen hier nicht nach Perfektion, richtig? Also bitte entsprechend verbessern. Beachten Sie, dass die Konvertierung von String in einen anderen Typ tatsächlich eine Form der Deserialisierung ist. Daher legen Sie Einschränkungen für Ihre Kunden (wer auch immer Sie die Strings angeben) zur Verfügung, um ihre Werte in bestimmten Formaten bereitzustellen. In diesem Fall werden die Formate durch das Verhalten der parse-Methoden definiert. Übung 1: Ändern Sie das Format zu einem späteren Zeitpunkt in abwärtskompatibler Weise, um den Zorn einer Person zu erleiden.

Jetzt machen wir die eigentliche Instanziierung:

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

Wir nehmen hier viele Abkürzungen, aber das ist nicht die Sixtinische Kapelle, die wir schaffen. Laden Sie einfach die Klasse und suchen Sie nach einem Konstruktor, dessen Parameteranzahl mit der Anzahl der Argumente (d. H. Arity) übereinstimmt. Überlastete Konstrukteure derselben Arität? Nein, ich werde nicht arbeiten. Varargs? Nein, ich werde nicht arbeiten. Nichtöffentliche Konstrukteure? Nein, ich werde nicht arbeiten. Und wenn Sie nicht garantieren können, dass Ihre Klasse einen Konstruktor bereitstellt, der alle Felder wie Ihr Beispiel TempStruct festlegt, nenne ich es einen Tag und schnappe mir ein Bier, denn dieser Ansatz ist DOA.

Wenn wir den Konstruktor gefunden haben, führen Sie eine Schleife über die Variablen String durch, um sie in die vom Konstruktor erwarteten Typen zu konvertieren. Wenn das funktioniert, rufen wir den Konstruktor über die Reflexion auf, bewegen den Zauberstab und sagen Abrakadabra. Voilà: Sie haben ein neues Objekt.

Versuchen wir es mit einem äußerst erfundenen Beispiel:

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

Ausgabe:

TempStruct 373.15 Kelvin

GLORIOUS

42
cambecc

Ich hatte das gleiche Problem und eine HashMap stellte sich als Lösung für mich heraus.

Probieren Sie es aus: http://docs.Oracle.com/javase/6/docs/api/Java/util/HashMap.html

2
TRU7H

Schauen Sie sich das Paket http://commons.Apache.org/beanutils/ an. Es ermöglicht den Zugriff auf Felder nach Namen.

0