2009-09-29 7 views
2

J'écris un service RPC basé sur la réflexion qui obtient les arguments transmis via une variété de mécanismes. Parfois, les arguments correspondent correctement au type de paramètre, parfois ce sont toujours des chaînes, et parfois ils sont enveloppés dans des objets "script" dynamiquement typés qui nécessitent l'extraction de la valeur appropriée.Méthode efficace pour convertir des objets inconnus en arguments Method.invoke appropriés?

Avant que je puisse method.invoke appeler, je dois construire la liste des arguments, quelque chose comme ceci:

Object a[] = new Object[method.parameterClasses.length]; 
for (int i = 0; i < a.length; ++i) 
{ 
    a[i] = prepare(method.parameterClasses[i], rpc.arguments[i]); 
} 

La méthode « préparer » ressemble à quelque chose comme:

Object prepare(Class clazz, Object o) 
{ 
    if (o == null) return null; 
    if (clazz == o.getClass()) return o; 
    if (clazz == String.class) return o.toString(); 

    // skip a bunch of stuff for converting strings to dates and whatnot 

    // skip a bunch of stuff for converting dynamic types 

    // final attempts: 
    try 
    { 
     return clazz.cast(o); 
    } 
    catch (Exception e) 
    { 
     return o;  // I give up. Try the invoke and hope for the best! 
    } 


} 

Au cours de tests unitaires , J'ai récemment été plutôt surpris de découvrir qu'une méthode passée un Integer en boîte qui s'attendait à une primitive longue échouait en fait la distribution et tombait par le bas, puis était correctement convertie par quelque chose pendant l'invocation(). Je supposais que l'appel à «jeter» ferait l'affaire. Existe-t-il un moyen d'exécuter et de vérifier explicitement la conversion d'argument effectuée normalement par invoke? En l'absence de cela, j'ai pensé mettre des contrôles explicites pour les types numériques, mais le nombre de permutations semblait hors de contrôle. Lorsque j'ajoute un support pour extraire des nombres des types dynamiques de script et convertir des chaînes, cela devient encore pire. J'envisage un tas de typechecks conditionnels pour chaque classe cible numérique possible, avec Integer.decode, Long.decode etc pour les arguments String, Short.decode et Number.intValue, Number.longValue, etc. pour les nombres.

Y a-t-il une meilleure façon de faire tout cela? Cela semblait une bonne approche au début, mais ça devient plutôt dégueulasse.

Répondre

2

Il est en effet surprenant, mais c'est le comportement actuel. Voir le bug 6456930.

En termes d'une meilleure façon d'aborder le problème, à la base, non, vous devrez définir toutes ces règles. Il y a de meilleurs modèles (plus maintenables) qu'une série de si, cependant. Pour commencer, j'aurais des objets de stratégie qui peuvent être invoqués pour un côté de ces objets (probablement le côté de la classe) afin que vous recherchiez l'objet approprié (disons à partir d'une carte) basé sur la classe, puis vous pouvez limiter vos conversions d'un côté (chaque objet serait préoccupé par la façon d'obtenir des choses dans cette classe particulière). De cette façon, quand il y a un nouveau type de conversion de classe que vous devez prendre en charge, vous pouvez écrire un objet pour cela, le tester séparément et simplement le brancher sur la carte sans changer de code.

1

Il n'y a vraiment pas de meilleur moyen. Il y a un nombre infini de conversions potentielles, mais seulement un petit nombre qui a vraiment du sens. Plutôt que d'essayer de donner une liberté totale aux clients, je recommande de spécifier un contrat pour la coercition de type.

Pour commencer, consultez les règles de conversion de type utilisées par JSP Expression Language. Elles sont assez puissantes et permettent de nombreux types de conversions, mais elles sont également bien définies, faciles à mémoriser — et peuvent être implémentées .

+0

Les conversions sensibles contraintes sont mon but, mais je dirais que les types numériques sont un domaine qu'il devrait être aussi flexible que possible, et juste la gestion de ceux-ci devient incontrôlable! –

+0

BTW, merci pour cette idée ... Je regarde les règles JSPEL .. et bien sûr, c'est une longue liste ennuyeuse de conversions conditionnelles. Soupir. Eh bien, j'aurais probablement pu l'écrire dans le temps qu'il m'a fallu pour taper cette question. :-) –

0

Il y a au moins quelques raccourcis que vous pouvez prendre pour simplifier votre code:

  • Si le paramètre est une instance de java.lang.Number et le type d'argument est soit byte, short, int, long, float ou double (soit comme primitive, soit comme classe wrapper), vous pouvez utiliser les méthodes de Number comme byteValue(), shortValue(), etc pour convertir le paramètre. Si le paramètre est une instance de java.lang.String et que le type d'argument est l'un de ceux mentionnés ci-dessus, vous pouvez utiliser la méthode staticOf (String) dans la classe correspondante pour la conversion.

La conversion d'autres types, et peut-être même propriétaires, nécessiterait bien plus de travail. Si c'est vraiment nécessaire et que votre méthode de préparation devient trop volumineuse, vous devriez peut-être envisager de faire abstraction de la conversion derrière une interface et permettre aux fournisseurs de conversion connectables d'être ajoutés dynamiquement à l'application.

Questions connexes