J'ai besoin de faire quelques appels de méthode réfléchis en Java. Ces appels incluront des méthodes qui ont des arguments qui sont des types primitifs (int, double, etc.). La façon de spécifier de tels types lors de la recherche de la méthode de manière réfléchie est int.class, double.class, etc.
Le défi est que j'accepte les entrées d'une source externe qui spécifiera les types dynamiquement. Par conséquent, je dois également proposer ces références de classe de manière dynamique. Imaginez un fichier délimité une liste de noms de méthodes avec des listes de types de paramètres:
doSomething int double
doSomethingElse Java.lang.String boolean
Si l'entrée était quelque chose comme Java.lang.String
, Je sais que je pourrais utiliser Class.forName("Java.lang.String")
pour cette instance de classe. Existe-t-il un moyen d'utiliser cette méthode, ou une autre, pour récupérer les classes de type primitif?
Edit: Merci à tous les répondants. Il semble clair qu'il n'y a aucun moyen intégré de faire proprement ce que je veux, donc je me contenterai de réutiliser la classe ClassUtils
à partir du framework Spring. Il semble contenir un remplacement pour Class.forName () qui fonctionnera avec mes exigences.
Le framework Spring contient une classe utilitaire ClassUtils qui contient la méthode statique forName. Cette méthode peut être utilisée dans le but exact que vous avez décrit.
Au cas où vous n'aimeriez pas avoir une dépendance à Spring: le code source de la méthode peut être trouvé e. g. ici sur leur référentiel public. Le code source de la classe est sous licence sous le modèle Apache 2.0.
Notez cependant que l'algorithme utilise une carte codée en dur des types primitifs.
Edit: Merci aux commentateurs Dávid Horváth et Patrick d'avoir signalé le lien rompu.
Les instances Class
pour les types primitifs peuvent être obtenues comme vous l'avez dit en utilisant par exemple int.class
, mais il est également possible d'obtenir les mêmes valeurs en utilisant quelque chose comme Integer.TYPE
. Chaque classe wrapper primitive contient un champ statique, TYPE
, qui a l'instance de classe primitive correspondante.
Vous ne pouvez pas obtenir la classe primitive via forName
, mais vous pouvez l'obtenir à partir d'une classe qui est facilement disponible. Si vous devez absolument utiliser la réflexion, vous pouvez essayer quelque chose comme ceci:
Class clazz = Class.forName("Java.lang.Integer");
Class intClass = clazz.getField("TYPE").get(null);
intClass.equals(int.class); // => true
Vous avez probablement juste besoin de mapper les primitives et pour le reste des classes, exécutez la méthode "forName":
Je ferais quelque chose comme:
void someWhere(){
String methodDescription = "doSomething int double Java.lang.Integer Java.lang.String"
String [] parts = methodDescription.split();
String methodName= parts[0]
Class [] paramsTypes = getParamTypes( parts ); // Well, not all the array, but a, sub array from 1 to arr.length..
Method m = someObject.class.getMethod( methodName, paramTypes );
etc. etc etc.
}
public Class[] paramTypes( String [] array ){
List<Class> list = new ArrayList<Class>();
for( String type : array ) {
if( builtInMap.contains( type )) {
list.add( builtInMap.get( type ) );
}else{
list.add( Class.forName( type ) );
}
}
return list.toArray();
}
// That's right.
Map<String,Class> builtInMap = new HashMap<String,Class>();{
builtInMap.put("int", Integer.TYPE );
builtInMap.put("long", Long.TYPE );
builtInMap.put("double", Double.TYPE );
builtInMap.put("float", Float.TYPE );
builtInMap.put("bool", Boolean.TYPE );
builtInMap.put("char", Character.TYPE );
builtInMap.put("byte", Byte.TYPE );
builtInMap.put("void", Void.TYPE );
builtInMap.put("short", Short.TYPE );
}
Autrement dit, créez une carte dans laquelle les types de primitives sont stockés et si la description appartient à une primitive, utilisez la classe mappée. Cette carte peut également être chargée à partir d'un fichier de configuration externe, pour ajouter de la flexibilité afin que vous ajoutiez String en tant que intégré au lieu de Java.lang.String ou possédez potentiellement une méthode comme celle-ci.
"doSomething string yes | no"
Il y a beaucoup de ce type de code dans les projets OS comme les bibliothèques Struts, Hibernate, Spring et Apache (pour n'en citer que quelques-uns), vous n'avez donc pas besoin de partir de zéro.
BTW. Je n'ai pas compilé le code ci-dessus, mais je suis sûr que cela fonctionne avec de petites modifications, ne votez pas pour cela.
Apache Commons Lang a ClassUtils.getClass (String) , qui prend en charge les types primitifs.
Un certain nombre de méthodes de classe ne gèrent malheureusement pas les primitives de manière cohérente. Une façon courante de contourner cela dans forName est d'avoir une table comme;
private static final Map<String, Class> BUILT_IN_MAP =
new ConcurrentHashMap<String, Class>();
static {
for (Class c : new Class[]{void.class, boolean.class, byte.class, char.class,
short.class, int.class, float.class, double.class, long.class})
BUILT_IN_MAP.put(c.getName(), c);
}
public static Class forName(String name) throws ClassNotFoundException {
Class c = BUILT_IN_MAP.get(name);
if (c == null)
// assumes you have only one class loader!
BUILT_IN_MAP.put(name, c = Class.forName(name));
return c;
}
Le code suivant explique comment obtenir la classe d'un type primitif dont le nom de champ est connu, par exemple dans ce cas, "sampleInt".
public class CheckPrimitve {
public static void main(String[] args) {
Sample s = new Sample();
try {
System.out.println(s.getClass().getField("sampleInt").getType() == int.class); // returns true
System.out.println(s.getClass().getField("sampleInt").getType().isPrimitive()); // returns true
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
}
}
class Sample {
public int sampleInt;
public Sample() {
sampleInt = 10;
}
}
On peut également vérifier si une valeur donnée est primitive ou non en obtenant sa classe wrapper respective ou sa valeur de champ.
public class CheckPrimitve {
public static void main(String[] args) {
int i = 3;
Object o = i;
System.out.println(o.getClass().getSimpleName().equals("Integer")); // returns true
Field[] fields = o.getClass().getFields();
for(Field field:fields) {
System.out.println(field.getType()); // returns {int, int, class Java.lang.Class, int}
}
}
}
Google Guava propose com.google.common.primitives.Primitives
pour ce genre de choses.