Planet Metax - Chaos & Logik

Java Reflections: Dynamisches Erstellen von Objekten

12. Jun 2010 Java Reflections: Dynamisches Erstellen von Objekten

Ich arbeite im Moment an dem interessanten Projekt, einen Traffic-Generator zu schreiben.

Dabei sollen die Zwischenankunftszeiten und Paketgrößen nach wählbaren Verteilungsfunktionen mit frei wählbaren Parametern verteilt sein.

Ich verwende die Java-Bibliothek JSimLib (eine Eigenentwicklung des Lehrstuhls), um Zufallszahlen nach bestimmten Verteilungen zu erhalten.

Da es einige Verteilungsklassen gibt (und diese möglicherweise noch ergänzt werden), und diese Verteilungsklassen unterschiedlich viele Parameter akzeptieren, wird ein Java-Machanismus benötigt, der ein Verteilungsobjekt mit der richtigen Verteilungklasse und den richtigen Parametern aus den Argumenten der Java-Kommandozeilenargumente erstellt.

Hierzu verwende ich die Reflections-Klassen von Java, die es ermöglichen, zur Laufzeit Klassen und Objekte zu untersuchen und zu erstellen.

Die einzelnen Schritte des Ansatzes:

Für den ersten Schritt muss eine Klasse mit dem entsprechenden Namen gesucht werden (alle Verteilungsklassen liegen in einem speziellen Paket), die kompatibel mit der Klasse RandomStream ist, und nicht abstrakt ist.

public static final String DistributionPrefix = "com.jSimLib.distributions."; public static boolean isDistribution(String classname) { try { Class<?> clazz = Class.forName(DistributionPrefix + classname); if (!RandomStream.class.isAssignableFrom(clazz)) return false; if ((clazz.getModifiers() & Modifier.ABSTRACT) != 0) return false; return true; } catch (ClassNotFoundException e) { return false; } }


Als nächstes wird ein Konstruktor dieser Klasse gesucht, der nur double-Argumente erwartet:

public static Class<?>[] getConstructorArguments(String classname) { try { Class<?> clazz = Class.forName(DistributionPrefix + classname); Constructor<?>[] constructors = clazz.getConstructors(); for (Constructor<?> c : constructors) { Class<?>[] args = c.getParameterTypes(); if (args.length == 0) continue; for (Class<?> arg : args) { if (!(arg.getName().equals("double") || arg.getName().equals("java.lang.Double"))) continue; } return args; } throw new IllegalArgumentException("Distribution class " + classname + " doesn't have a valid non-empty constructor."); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("Argument must be a valid RandomStream classname."); } }


Um den Konstruktor dann tatsächlich aufzurufen und ein Objekt der Klasse zu erhalten, kann folgende Methode benutzt werden:

public static RandomStream buildDistribution(String classname, Class<?>[] argtypes, Object[] args) { try { Class<?> clazz = Class.forName(DistributionPrefix + classname); Constructor<?> constructor = clazz.getConstructor(argtypes); return (RandomStream) constructor.newInstance(args); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("Argument must be a valid RandomStream classname."); } catch (NoSuchMethodException e) { throw new IllegalArgumentException("Distribution class " + classname + " doesn't have a valid constructor with " + args.length + " double arguments."); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("Distribution class " + classname + " could not be constructed:", e); } catch (InstantiationException e) { throw new IllegalArgumentException("Distribution class " + classname + " could not be constructed:", e); } catch (IllegalAccessException e) { throw new IllegalArgumentException("Distribution class " + classname + " could not be constructed:", e); } catch (InvocationTargetException e) { throw new IllegalArgumentException("Distribution class " + classname + " could not be constructed:", e); } catch (ClassCastException e) { throw new IllegalArgumentException("Distribution class " + classname + " could not be constructed:", e); } }


Insgesamt kann nun also mit diesen drei statischen Methoden ein Objekt einer beliebigen Verteilungsklasse erstellt werden.

Um also beispielsweise ein Objekt der Normalverteilung mit den Parametern 5 und 0.5 zu erhalten, können die Methoden folgendermaßen aufgerufen werden:

String classname = "NormalStream"; // Mean: 5.0, Standard deviation: 0.5 double[] args = { 5.0, 0.5 }; if (DistBuilder.isDistribution(classname)) { Class<?>[] signature = DistBuilder.getConstructorArguments(classname); if (signature.legth == 2) { // Yes, it is ;-) try { RandomStream dist = DistBuilder.buildDistribution(classname, signature, args); // Get random numbers // ... } catch (IllegalArgumentException e) { e.printStackTrace(); } } }


Natürlich wird das Verfahren erst interessant, wenn Klassenname und Argumente zur Compilezeit nicht feststehen und dynamisch erzeugt werden.

Aber das überlasse ich den Programierkünsten des Lesers ...

DistBuilder.java
Java Quell-Datei
Größe: 3.0 Kilobyte

Schlagworte: Java, OOP, Programmierung, Technik
Veröffentlicht am 12.06.2010 11:54 in Java | Keine Kommentare »

Kommentare

Es sind noch keine Kommentare vorhanden

Kommentar schreiben:

Mit (*) gekennzeichnete Felder sind optional.
BBCode im Kommentarfeld erlaubt.