Dynamisches Laden von Klassen ● ● Die Klasse class und die Klassen des Packages java.lang.reflect erlauben Reflection und Intospection. Klassen können zur Laufzeit geladen, untersucht und instanziert werden. Zu den dann erzeugten Objekten können Methoden aufgerufen werden. 1 Die Klasse Class ● ● ● ● ● Ein Objekt der Klasse Class beschreibt zur Laufzeit eine Klasse In diesem Objekt sind die stitschen Variblen der Klasse angelegt. Zu jeder im java-Programm benutzen Klasse gibt es ein solches Objekt der Klasse Class Ein Objekt der Klasse Class bekommt man von einem Objekt über den Aufruf von getClass(). Die Methode Class.forName(Sting classname) lädt eine Klasse und gibt ein Objekt der Klasse Class zurück. 2 Untersuchen einer Klasse ● ● ● Die Klasse und das Package java.lang.reflect stellen Methoden und Klassen zur Untersuchung einer Klasse bereit. ● public Constructor<?>[] getConstructors() ● public Method[] getDeclaredMethods() – public int getModifiers() – public Class<?>[] getParameterTypes() . . . So wird es möglich, die Eigenschaften einer Klasse und ihrer Methoden komplett auszulesen. 3 Beispiel zum Auslesen von Informationen Aus einem Objekt Class Class C=Class.forName(ClassName); if (C.isInterface()) { Out=Modifier.toString(C.getModifiers())+C.getName(); } else { Out=Modifier.toString(C.getModifiers()) +" class "+C.getName()+"\n" +"extends "+C.getSuperclass().getName()+"\n"; Class[] Interfaces=C.getInterfaces(); if (Interfaces!=null & Interfaces.length>0) { Out+="implements "; for (int i=0; i<Interfaces.length; i++) { if (i>0) Out+=", "; Out+=Interfaces[i].getName(); } Out+="\n"; } Out+="{\n"; 4 Field[] Fields=C.getDeclaredFields(); if (Fields.length>0) Out+=" // DataMember\n"; for (int i=0; i<Fields.length;i++) Out+=" "+Fields[i].toString()+";\n"; Constructor[] Constructors=C.getDeclaredConstructors(); if (Constructors.length>0) Out+=" // Constructors\n"; for (int i=0; i<Constructors.length; i++) Out+=" "+Constructors[i].toString()+";\n"; Method[] Methods=C.getDeclaredMethods(); if (Methods.length>0) Out+=" // Methods\n"; for (int i=0; i<Methods.length;i++) Out+=" "+Methods[i].toString()+";\n"; Class[] Classes=C.getClasses(); if (Classes.length>0) Out+=" // Classes / Interfaces\n"; for (int i=0; i<Classes.length;i++) Out+=" "+Classes[i].toString()+";\n"; Out+="}\n"; } } 5 Laden einer Klasse Class X=Class.forName(Name); Instanzieren der Klasse Calculate C=(Calculate)(X.newInstance()); Metodenaufruf System.out.println("Y(x)="+C.fVonX(x)); Caclulate ist ein Interface public interface Calculate { public double fVonX(double x); } 6 Beispiel: Ausdrucksberechnung ● ● ● ● Ein Programm solle in der Lage sein, beliebige Ausrücke f(x) zu berechnen. Lösungsansatz 1: Ausdrucksberechner mit Verfahren des rekursiven Abstiegs programmieren. Lösungsansatz 2: Verwendung von Jcup/Jlex um einen Interpreter für Ausdrücke generieren zu lassen Lösungsansatz 3: Generieren eines Javaquelltextes einer Klasse mit der Methode fvonx, Übersetzung, Laden, Instanzieren der Klasse und Ausfüh7 rung der Methode fvonx. Erzeugen des Quelltextes ● ● Class.forName funktioniert für jede zu ladende Klasse nur ein mal. Workaround: fortlaufend nummerierter Dateiname // Hier wird der Name zusammengebaut Name="dcalc"+Count; FileWriter O=new FileWriter(Name+".java"); O.write("class "+Name+" implements Calculate\n"); O.write("{public double fVonX(double x)“); O.write("{return "+Command+";}}"); O.close(); 8 Compilieren Process p = Runtime.getRuntime() .exec("javac "+Name+".java"); System.out.println("Compilation of "+Name+" started"); p.waitFor(); String line; BufferedReader input = new BufferedReader(new InputStreamReader (p.getErrorStream())); while ((line = input.readLine()) != null) { System.out.println(line); } input.close(); 9 Laden und instanzieren void calculate(double x) { /* Durch Einfuehrung der Funktion calculate verliert das Objekt Class X nach jeder Ausfuehrung sein Leben wird also jedesmal neu angelegt. */ try { Class X=Class.forName(Name); Calculate C=(Calculate)(X.newInstance()); System.out.println("Y(x)="+C.fVonX(x)); } catch(Exception e) {System.out.println("Exception " + e);} } 10 Der Classloader ● ● ● ● Jede Klasse wird nur einmalig geladen. Ausweg, Benutzung eines anderen Classloaders: URLClassLoader Wird mit einem Array von URLs instanziert. Erzeugte Klasse muss in einem Unterverzeichnis liegen, davon URL kreieren, damit URKClassLoader instanzieren, damit Klasse laden - fertig. 11 URL bauen // Get the directory (URL) of the reloadable class URL[] urls = null; try { // Convert the file object to a URL File dir = new File(System.getProperty("user.dir") +File.separator+"XFX"+File.separator); URL url = dir.toURI().toURL(); System.out.println("URL:"+url); urls = new URL[]{url}; } catch (MalformedURLException e) { System.out.println(e); } 12 Classsloader instanzieren und rechnen try { // Create a new class loader with the directory ClassLoader cl = new URLClassLoader(urls); //java.net // Load in the class Class cls = cl.loadClass("dcalc"); // Create a new instance of the new class C = (Calculate)cls.newInstance(); } catch (IllegalAccessException e) {System.out.println(e);} catch (InstantiationException e) {System.out.println(e);} catch (ClassNotFoundException e) {System.out.println(e);} System.out.println("Y="+Command); System.out.println("Y(x)=Y("+x+")="+C.fVonX(x)); 13