2013-05-17 3 views
1

Je veux obtenir un objet sérialisé en appelant cette méthode:renvoie l'objet sérialisé (s'il existe) sinon renvoie une nouvelle instance?

ArrayList<String> myArrayList = (ArrayList<String>) getSerializedObject(ArrayList.class, "arraylist.ser"); 

Si le fichier spécifié (arraylist.ser) n'existe pas, ou ne correspond pas à la classe je suis passé, je veux revenir un nouveau instance de cette classe.

private Object getSerializedObject(Class<?> c, String filename) { 
    Object serObject = null; 

     try { 
      if (new File(filename).exists()) { 
       ObjectInputStream in = new ObjectInputStream(new FileInputStream(filename)); 
       Object tempObj = in.readObject(); 
       if (tempObj.getClass().equals(c)) { 
        System.out.println("Loading "+filename); 
        serObject = tempObj; 
       } 
       in.close(); 
      } 
     } 
     catch (FileNotFoundException e) { e.printStackTrace(); } 
     catch (IOException e) { e.printStackTrace(); } 
     catch (ClassNotFoundException e) { e.printStackTrace(); } 

     if (serObject != null) { 
      return serObject; 
     } 
     else { 
      // return new instance of Class c here 
     } 
} 

Répondre

1

Dans votre méthode, vous acceptez c comme entrée, ce qui est un type inconnu représenté par un objet Class<?>. Si le type représenté par c a un public constructeur sans arg, vous pouvez créer une nouvelle instance en appelant:

try { 
    return c.newInstance(); 
} catch (Exception e) { 
    // no public, no-arg constructor 
} 

Si elle n'a pas, alors vous aurez une exception. Si vous ne voulez pas croiser les doigts et devinez et que vous voulez plutôt que de savoir ce que les constructeurs sont accessibles pour vous, l'API de réflexion vous donnera cette information:

Constructor[] publicConstructors = c.getConstructors(); // may have .length == 0 
for (Constructor ctor : publicConstructors) { 
    // find the one you want 
} 

Vous pouvez ensuite inspecter les objets pour voir ce que les types d'arguments sont requis pour chacun. Lorsque vous trouvez le constructeur souhaité, vous pouvez appeler newInstance(Object ...) sur l'objet Constructor et transmettre les valeurs d'argument appropriées.

Cependant, même avec cette information, vous devez fournir les valeurs appropriées. Pour résoudre ce problème pour "n'importe quelle classe" est extrêmement difficile. Si vous pouvez restreindre les valeurs nécessaires à zéro ou juste quelques valeurs spécifiques dans un ordre connu, alors cela devient un problème plus facile à résoudre, mais cela restreint sévèrement le nombre ou le type d'objets que votre fonction est réellement capable de construire.

Gardez à l'esprit ces choses:

Votre méthode accepte toute Class<?>, qui comprend interface s tels que List. Les interfaces ne peuvent pas être instanciées et n'ont aucun constructeur. Votre exemple évite ce cas de bordure en utilisant l'implémentation concrète ArrayList.

Constructors ne sont pas héritées en Java, même si vous vérifiez que c est une sous-classe de ArrayList vous ne pouvez pas supposer sans risque que c a les mêmes signatures de constructeur que ArrayList. (Autrement dit, sauf si c'est vraiment la même classe: ArrayList.class.equals(c).)

Si ce code est réécrite pour dire qu'il ne désérialise List (ou spécifiquement ArrayList) au lieu d'accepter tout type, vous pouvez simplement appeler le constructeur ArrayList régulier et vous épargner quelques problèmes.

Mais même si vous faites cela, votre code ne vérifie pas que la liste désérialisée est ArrayList<String>. Qu'est-ce qui sort du processus de désérialisation sera ArrayList<?>, ou List<?>, ou peut-être quelque chose qui n'est même pas List du tout. En supposant que vous ayez eu la chance que le fichier contienne vraiment un ArrayList, la seule façon de savoir que la liste "ne contient que String" est de vérifier chaque objet de la liste après l'avoir désérialisé.

L'application générique se produit au moment de la compilation et la désérialisation à l'exécution ne vérifie pas que le type paramétré est sans danger pour la sécurité. C'est pourquoi le compilateur émet un avertissement à propos de la fonte dangereuse:

Object out = getSerializedObject(ArrayList.class, "arraylist.ser"); 
ArrayList<?> unboundedList = (ArrayList<?>) out; // verifies that 'out' is really an ArrayList 
ArrayList<String> myArrayList = 
    (ArrayList<String>) unboundedList; // UNSAFE! Does not verify the list's contents! 
+0

Wow, c'est une bonne information. J'ai choisi ta réponse. Je vous remercie. – BLuFeNiX

2

En utilisant réflexion vous pouvez créer de nouvelles instances de classes. Je vous dirigerais vers le paquet, mais vous utilisez déjà la classe Class.

Bien sûr, vous devrez probablement choisir un constructeur approprié, si celui par défaut n'existe pas.

2

Si vous connaissez déjà la classe et si toutes les classes ont la même signature constructeur, vous pouvez le faire avec la réflexion.

Ce qui suit est un défaut, aucun constructeur d'arguments

Class<?>[] args = {}; 
Constructor<?> constructor = c.getConstructor(args); 
Object inst = constructor.newInstance((Object[])args); 
+0

Cela a fonctionné parfaitement, merci! – BLuFeNiX

Questions connexes