2008-10-07 9 views
44

Ich muss einige reflektive Methodenaufrufe in Java durchführen. Diese Aufrufe enthalten Methoden mit primitiven Datentypen (int, double usw.). Die Art, solche Typen anzugeben, wenn die Methode reflektiv nachgeschlagen wird, ist int.class, double.class, usw.Dynamische Suche nach der Klasse, die einen primitiven Java-Typ darstellt

Die Herausforderung besteht darin, dass ich Eingaben von einer externen Quelle akzeptiere, die die Typen dynamisch angeben. Daher muss ich auch diese Klassenreferenzen dynamisch erstellen. Stellen Sie sich eine getrennte Datei eine Liste von Methodennamen mit Listen von Parametertypen:

doSomething int double 
doSomethingElse java.lang.String boolean 

Wenn die Eingabe etwas wie java.lang.String war, weiß ich, ich Class.forName("java.lang.String") diese Klasse Instanz zurück nutzen könnte. Gibt es eine Möglichkeit, diese Methode oder eine andere Methode zu verwenden, um die primitiven Klassen zurückzubekommen?

Bearbeiten: Vielen Dank an alle Befragten. Es scheint klar zu sein, dass es keine eingebaute Möglichkeit gibt, sauber zu machen, was ich will, also werde ich mich damit begnügen, die Klasse ClassUtils aus dem Spring-Framework wiederzuverwenden. Es scheint einen Ersatz für Class.forName() zu enthalten, der mit meinen Anforderungen funktioniert.

Antwort

18

Das Spring-Framework enthält eine Utility-Klasse ClassUtils, die die statische Methode forName enthält. Diese Methode kann genau zu dem Zweck verwendet werden, den Sie beschrieben haben.

Falls Sie keine Abhängigkeit von Spring haben möchten: die source code of the method kann e gefunden werden. G. here in ihrem öffentlichen Repository. Der Klassenquellcode wird unter dem Apache 2.0-Modell lizenziert.

Beachten Sie jedoch, dass der Algorithmus eine fest codierte Karte primitiver Typen verwendet.


Edit: Dank commen Dávid Horváth und Patrick für den Hinweis den Defekten Link aus.

+4

Die Quelle für die ClassUtils.java-Datei scheint an dieser Verknüpfung unterbrochen werden. [Dieser schaut nach oben, zu mir] (http://www.jarvana.com/jarvana/view/org/springframework/spring-core/3.1.0.RELEASE/spring-core-3.1.0.RELEASE-sources. jar! /org/springframework/util/ClassUtils.java? format = ok) – Patrick

+1

@Patrick: Leider ist dein Link auch kaputt gegangen. Das Spring-Framework ist jetzt jedoch auf GitHub verfügbar. https://github.com/spring-projects/spring-framework/blob/master/spring-core/src/main/java/org/springframework/util/ClassUtils.java –

23

Die Class Instanzen für die primitiven Typen sind wie gesagt erhältlich, wobei z. int.class, aber es ist auch möglich, die gleichen Werte mit etwas wie Integer.TYPE zu erhalten. Jede primitive Wrapper-Klasse enthält ein statisches Feld , das die entsprechende primitive Klasseninstanz besitzt.

Sie können die primitive Klasse nicht über forName erhalten, aber Sie können es von einer Klasse erhalten, die leicht verfügbar ist. Wenn Sie unbedingt Reflexion verwenden müssen, können Sie so etwas wie dies versuchen:

Class clazz = Class.forName("java.lang.Integer"); 
Class intClass = clazz.getField("TYPE").get(null); 

intClass.equals(int.class);   // => true 
+0

Das ist wahr, und ich war mir dessen nicht bewusst. Allerdings muss ich in meiner Situation eine Methode zur Angabe jeder Methode reflektiv nennen. Wenn ich also java.lang.Integer akzeptiere, um int-Werte darzustellen, verliere ich die Möglichkeit, Methoden aufzurufen, die Integer-Werte akzeptieren. –

+0

Sie können nur testen, um zu sehen, ob Sie irgendeinen beliebigen speziellen Typ erhalten haben (sagen wir: "int") und dann das Neuschreiben entsprechend machen. Es gibt keinen Zeichenfolgenwert, den Sie forName einspeisen können, um die Klasse eines primitiven Typs abzurufen. –

+0

Ok, danke. Ihre Antwort war hilfreich. –

16

Wahrscheinlich brauchen Sie nur die Grundelemente abzubilden und für den Rest der Klassen der „forName“ Methode ausführen:

Ich möchte etwas tun:

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

Das heißt, erstellen Sie eine Karte, wo Die Primitiventypen werden gespeichert. Wenn die Beschreibung zu einem Primitiv gehört, verwenden Sie die zugeordnete Klasse. Diese Map kann auch aus einer externen Konfigurationsdatei geladen werden, um die Flexibilität zu erhöhen, so dass Sie String als ein eingebautes anstelle von java.lang.String hinzufügen oder eine ähnliche Methode haben.

"doSomething String yes | no"

Es gibt viele dieser Art von Code in OS Projekten wie Struts, Hibernate, Spring und Apache Libs (um nur einige zu nennen), so dass Sie nicht brauchen, Von Null anfangen.

BTW. Ich habe den obigen Code nicht kompiliert, aber ich bin mir ziemlich sicher, dass es mit kleinen Modifikationen funktioniert, stimmt mich nicht dafür ab.

+0

Dies ist, was auch innerhalb von ObjectInputStream getan wird Es führt einfach eine forName() - Suche durch und wenn eine ClassNotFoundException ausgelöst wird, wird eine Map-Suche durchgeführt. – Robin

+0

Danke für die Antwort. Ich werde die Hilfsmethode in Spring ClassUtils wiederverwenden, die diesen Nachschlageprozess verwendet. –

+1

Wo verwenden Sie diesen "Low Level" -Code? Sehen Sie sich genauer an, wie Sie Ihren Dienst sichern können, andernfalls könnten Sie einen "System.exit()" - Aufruf von Ihrem Remote-Client erhalten, und glauben Sie mir, der Server wird heruntergefahren! – OscarRyz

3

Eine Reihe von Klassenmethoden behandeln die Primitiven leider nicht konsistent. Ein üblicher Weg in forName ist eine Tabelle wie:

2

Der folgende Code beschreibt, wie die Klasse eines primitiven Typs erhalten wird, dessen Feldname bekannt ist, z. in diesem Fall '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; 
    } 
} 

Man kann auch prüfen, ob ein bestimmte Wert primitiv ist oder nicht, indem sie die entsprechende Wrapper-Klasse bekommen oder es ist Feldwert.

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} 
      } 
     } 
    } 
+0

Danke für das Posten einer Antwort! Während ein Code-Snippet die Frage beantworten kann, ist es immer noch großartig, zusätzliche Informationen hinzuzufügen, wie zum Beispiel Erklärungen. – j0k

+0

@ j0k Ich habe dem Code eine Erklärung hinzugefügt. Bitte prüfe. – Arham

+0

das ist viel besser, danke – j0k

0

Google Guava bietet com.google.common.primitives.Primitives für diese Art von Sachen.