web-dev-qa-db-fra.com

Créer dynamiquement un objet en Java à partir d'un nom de classe et définir des champs de classe à l'aide d'une liste avec des données

J'ai une liste qui contient des données avec le type String -> ["classField1", "classField2", "classField3"]

J'ai une méthode (myMethod(List list, String className)) qui accepte en paramètre la liste. Ainsi, je peux transmettre cette liste via le paramètre à myMethod (Liste de liste, String className). 

Dans myMethod, je veux créer un objet, qui sera une instance du className, c'est-à-dire le deuxième paramètre. Après cela, je veux définir les champs de la classe en utilisant les données de la liste. En raison du fait que je souhaite obtenir dynamiquement les champs de la classe, le résultat de ce qui précède est que je dois convertir chaque valeur String de la liste en fonction du type de chaque champ de la classe.

Je suis sûr que l'ordre des chaînes à l'intérieur de la liste est dans le bon ordre et correspond aux champs de la classe avec le même ordre.

Quelqu'un a-t-il une idée de la manière de procéder?

Exemple:

["StringtempValue", "StringUnitOfMeasurement"] =>

Créer un objet d'instance:

public class TempStruct {

   private double tempValue;
   private String unitOfMeasurement;

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

}

J'essaie de donner une solution de la manière suivante:

En fait, je veux créer un objet d'une classe existante et j'ai essayé de le faire avec réflexion. J'utilise le code suivant:

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

Mais je reçois une exception à la deuxième ligne, quand il essaie de créer le nouvel objet . Comme @JB Nijet a dit que je ne savais pas que la méthode getDeclaredFields () ne retournait pas les champs triés.

En fait, j'ai une méthode qui n'accepte que la liste de chaînes, donc en utilisant la réflexion, je convertis l'objet en liste de chaînes, puis je veux faire le contraire. Je ne pensais pas d'autre moyen de le faire. 

13

L'instanciation dynamique des objets peut devenir assez complexe et votre scénario aborde plusieurs aspects:

  • convertir les valeurs d'objet de String au type approprié
  • charger la bonne classe à partir du nom de la classe et créer une instance
  • assigner ces valeurs à l'objet

Une discussion approfondie de chacun de ces points occuperait un chapitre entier dans un traitement fascinant de Java en tant que langage dynamique. Mais, en supposant que vous n’ayez pas le temps d’apprendre ces subtilités, ou que vous deveniez dépendant d’une immense bibliothèque tierce partie, préparons-nous quelque chose qui vous mettra sur la bonne voie. S'il vous plaît, gardez vos mains à l'intérieur du véhicule à tout moment, car le trajet sera cahoteux.

Abordons d'abord le problème de la conversion de type. Les valeurs sont fournies sous la forme Strings, mais votre objet les stockera sous la forme double, long, int, etc. Nous avons donc besoin d'une fonction qui analyse une String dans le type de cible approprié:

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

Pouah. C'est moche et ne gère que les types intrinsèques. Mais nous ne cherchons pas la perfection ici, non? Alors s'il vous plaît améliorer le cas échéant. Notez que la conversion de String en un autre type est en réalité une forme de désérialisation. Par conséquent, vous imposez des contraintes à vos clients (celui qui vous donne la Strings) pour fournir leurs valeurs dans des formats spécifiques. Dans ce cas, les formats sont définis par le comportement des méthodes parse. Exercice 1: À un moment donné, changez le format de manière incompatible avec le passé pour provoquer la colère de quelqu'un.

Faisons maintenant l'instanciation réelle:

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

Nous prenons beaucoup de raccourcis ici, mais bon, ce n'est pas la chapelle Sixtine que nous créons. Il vous suffit de charger la classe et de rechercher un constructeur dont le nombre de paramètres correspond au nombre d'arguments (c'est-à-dire, arity). Des constructeurs surchargés de la même arité? Non, ça ne marchera pas. Varargs? Non, ça ne marchera pas. Les constructeurs non publics? Non, ça ne marchera pas. Et si vous ne pouvez pas garantir que votre classe fournira un constructeur qui définit tous les champs comme votre exemple TempStruct le fait, je l'appellerai un jour et je prendrai une bière, car cette approche est celle de DOA.

Une fois le constructeur trouvé, parcourez les arguments String pour les convertir en types attendus par le constructeur. En supposant que cela fonctionne, nous invoquons ensuite le constructeur par réflexion, agitons la baguette magique et disons abracadabra. Voilà: vous avez un nouvel objet.

Essayons avec un exemple extrêmement artificiel:

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

Sortie:

TempStruct 373.15 Kelvin

GLORIEUX

42
cambecc

J'avais le même genre de problème et un hashMap s'est avéré être la solution pour moi.

Découvrez-le: http://docs.Oracle.com/javase/6/docs/api/Java/util/HashMap.html

2
TRU7H

Jetez un coup d’oeil au paquet http://commons.Apache.org/beanutils/ . Il permet d'accéder aux champs par leur nom.

0
Stepan Yakovenko