Java SE 8 Standard-Bibliothek  
Professionelle Bücher. Auch für Einsteiger.
 
Inhaltsverzeichnis


Vorwort
1 Neues in Java 8 und Java 7
2 Fortgeschrittene String-Verarbeitung
3 Threads und nebenläufige Programmierung
4 Datenstrukturen und Algorithmen
5 Raum und Zeit
6 Dateien, Verzeichnisse und Dateizugriffe
7 Datenströme
8 Die eXtensible Markup Language (XML)
9 Dateiformate
10 Grafische Oberflächen mit Swing
11 Grafikprogrammierung
12 JavaFX
13 Netzwerkprogrammierung
14 Verteilte Programmierung mit RMI
15 RESTful und SOAP-Web-Services
16 Technologien für die Infrastruktur
17 Typen, Reflection und Annotationen
18 Dynamische Übersetzung und Skriptsprachen
19 Logging und Monitoring
20 Sicherheitskonzepte
21 Datenbankmanagement mit JDBC
22 Java Native Interface (JNI)
23 Dienstprogramme für die Java-Umgebung
Stichwortverzeichnis

Jetzt Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Java SE 8 Standard-Bibliothek von Christian Ullenboom
Das Handbuch für Java-Entwickler
Buch: Java SE 8 Standard-Bibliothek

Java SE 8 Standard-Bibliothek
Pfeil 18 Dynamische Übersetzung und Skriptsprachen
Pfeil 18.1 Codegenerierung
Pfeil 18.1.1 Generierung von Java-Quellcode
Pfeil 18.1.2 Codetransformationen
Pfeil 18.1.3 Erstellen von Java-Bytecode
Pfeil 18.2 Programme mit der Java Compiler API übersetzen
Pfeil 18.2.1 Java Compiler API
Pfeil 18.2.2 Fehlerdiagnose
Pfeil 18.2.3 Eine im String angegebene Kompilationseinheit übersetzen
Pfeil 18.2.4 Wenn Quelle und Ziel der Speicher sind
Pfeil 18.3 Ausführen von Skripten
Pfeil 18.3.1 Java-Programme mit JavaScript schreiben
Pfeil 18.3.2 Kommandozeilenprogramme jrunscript und jjs
Pfeil 18.3.3 javax.script-API
Pfeil 18.3.4 JavaScript-Programme ausführen
Pfeil 18.3.6 Alternative Sprachen für die JVM
Pfeil 18.3.7 Von den Schwierigkeiten, dynamische Programmiersprachen auf die JVM zu bringen *
Pfeil 18.4 Zum Weiterlesen
 
Zum Seitenanfang

18Dynamische Übersetzung und Skriptsprachen Zur vorigen ÜberschriftZur nächsten Überschrift

»Wenn einer einen wirklich klaren Gedanken hat,
kann er ihn auch darstellen.«
– Michel de Montaigne (1533–1592)

Für Java-Entwickler besteht der Alltag im Allgemeinen darin, neuen Java-Quellcode zu schreiben und eigenen oder fremden Quellcode zu lesen, zu verstehen und zu ändern. Nun ist Java als Programmiersprache aber nicht immer die effizienteste Darstellungsform für einen Algorithmus oder ein Geschäftsproblem. Es gibt je nach Anwendungsfall kompaktere und bessere Ausdrucksformen. So kann eine grafische Notation für gewisse Problemstellungen sinnvoller sein als eine Gruppe von Java-Klassen, oder Tests können vielleicht besser in natürlicher Sprache formuliert werden. Installer oder Dialoge könnten alternativ in XML beschrieben werden. Besonders Skriptsprachen haben deutlich kompaktere Ausdrucksformen als Java. Ein Trend sind so genannte Domain Specific Languages (DSL). Das sind Spezial-Sprachen, die nur für ein ganz gewisses Problem definiert wurden und optimal ein Problem lösen sollen. So ist PDF eine optimale DSL für die Beschreibung von zweidimensionalen Grafiken, und Shell-Skripte sind perfekt zum Automatisieren von Vorgängen. Die Lösung hätte durchaus mit Java formuliert werden können, jedoch ist Java hierzu nicht optimal geeignet. Java ist als universell einsetzbare Programmiersprache eher das Gegenteil einer DSL. Und das Ziel sollte immer sein, das beste Werkzeug für eine Aufgabe zu wählen und viel zu automatisieren.

Da unabhängig von der Darstellungsform aber letztendlich eine Java-Laufzeitumgebung das Programm abarbeiten muss, stellt sich die Frage, wie die unterschiedlichen Darstellungsformen wie eine DSL ausgeführt werden, denn die JVM versteht nur Bytecode. Hier bieten sich zwei Wege an:

  • Ein Generator überführt die Darstellungsform in Java-Quellcode, der später wie die anderen Java-Klassen eines Projekts übersetzt wird. In einer erweiterten Form wird nicht nur Java-Quellcode erzeugt, sondern mit einem nachgeschalteten Compiler der Programmcode gleich übersetzt, sodass am Ende Klassendateien stehen oder sogar ein Java-Archiv. Die generierten Java-Klassen können vor dem Entwickler verborgen werden, indem sie zwar temporär generiert, aber nach dem Übersetzungsvorgang sofort wieder gelöscht werden. Der Nachteil des Generator-Ansatzes ist, dass im Build-Prozess ein extra Schritt nötig ist. Da der Entwickler aber immer sieht, was der Generator macht, ist dieser Prozess gut zu verstehen.

  • Die Beschreibung wird zur Laufzeit über einen Interpreter (oder Generator) verarbeitet. Es entstehen möglicherweise auch neue Klassendateien (vielleicht sogar mit dem Java-Standard-Compiler), aber das Generieren findet zur Laufzeit statt und nicht wie im ersten Fall vor dem Start des Programms. Es ist ein wenig mit der JVM vergleichbar, die Java-Programme in Maschinencode übersetzt. Traditionelle Compiler machen das vor dem Programmstart, und eine moderne JVM übersetzt dynamisch zur Laufzeitzeit.

In diesem Kapitel schauen wir uns genau diese Bereiche an. Zunächst wollen wir sehen, welche Möglichkeiten es gibt, Java-Programmcode zu generieren. Ob vor dem Programmstart oder dynamisch ist dann unerheblich. Der zweite Schritt soll das Problem lösen, wie (generierter) Java-Programmcode in Java-Bytecode übersetzt wird. Der letzte Teil des Kapitels stellt Skriptsprachen vor, von denen einige rein interpretiert werden und andere zur Laufzeit Bytecode erstellen, sodass sie von den Optimierungsmöglichkeiten der JVM profitieren.

 
Zum Seitenanfang

18.1Codegenerierung Zur vorigen ÜberschriftZur nächsten Überschrift

Der Ausgangspunkt der Codegenerierung ist ein Artefakt, das ein Übersetzer in Java-Quellcode oder Java-Bytecode überführt. Ob das Ergebnis Quellcode oder direkt Java-Bytecode ist, hat verschiedene Vor- und Nachteile:

Vorteil

Nachteil

Generierung von Quellcode

  • Der Quellcode ist einsehbar.

  • leichter zu debuggen

  • Der Generator ist leicht zu schreiben.

  • Ein Compiler ist erforderlich.

  • Die Übersetzung kostet Zeit und Speicher.

Direkte Bytecode-Generierung

  • sehr performant

  • Es ist kein Java-Compiler nötig.

  • sinnvoll bei Bytecode-zu-Bytecode-Transformationen

  • Der Bytecode ist schlecht einsehbar.

  • Das Debugging ist schwierig.

  • Der Generator ist schwer zu schreiben, da Wissen über Bytecode nötig ist.

Tabelle 18.1Vor- und Nachteile vom Compiler und von direkter Bytecode-Erzeugung

Generatoren, die – aus welchem Zielformat auch immer – Java-Quellcode generieren, sind relativ einfach zu entwickeln. Der Aufwand besteht in der Entwicklung des Parsers, aber das Abbilden in einen Quellcode-String ist einfach. Ganz anders sieht es bei der Bytecode-Ausgabe aus. Zwar vereinfachen Bibliotheken das Generieren von Bytecode, doch bleibt eine deutlich höhere Komplexität bestehen. Der Vorteil ist jedoch, dass das Generieren und Verändern vom Bytecode recht performant ist und so problemlos zur Laufzeit vorgenommen werden kann. Sogar beim Laden der Klassen lassen sich Bytecode-Modifizierungen vornehmen, und in der Laufzeit macht sich das nicht auffällig negativ bemerkbar.

 
Zum Seitenanfang

18.1.1Generierung von Java-Quellcode Zur vorigen ÜberschriftZur nächsten Überschrift

Wir haben gesehen, dass ein Generator entweder Java-Quellcode oder Java-Bytecode generieren kann. Soll die Ausgabe ein Java-Programm im Quellcode sein, so lassen sich unterschiedliche Techniken zur Erzeugung nutzen:

Quellcode als Text

Quellcode über abstrakte Repräsentation

Einfach

Template

Konkatenation von Strings oder Ausgaben über printXXX(…)

Ein Template enthält Platzhalter, die später gefüllt werden.

Es wird kein String erzeugt, sondern das Java-Programm wird über eine API aufgebaut.

Nach dem Aufbau kann eine String-Repräsentation erzeugt werden.

Tabelle 18.2Möglichkeiten zur Erzeugung von Quellcode

Generieren von Text

Im ersten Fall wird Java-Quellcode als String behandelt, der etwa über Konkatenation zusammengesetzt wird. Das kann so aussehen:

String generate( String classname, String output ) {
return "public class " +
classname +
"{ public static void main(String[] args){System.out.println(\"" +
output + "\");}}";
}

Der Nachteil ist offensichtlich und erinnert an Servlets, die HTML ausgeben: Das Programm selbst lässt sich kaum ändern, und der Programmautor verheddert sich schnell beim Escapen der Anführungsstriche. Besser ist es, das Java-Programm als Template zu nehmen und später Platzhalter mit den tatsächlichen Daten zu füllen. Die Java-Bibliothek bietet hier schon einen einfachen Mechanismus über String.format(…):

String generate( String classname, String output ) {
String template = "public class %1$s {" +
"public static void main(String[] args)" +
"{System.out.println(\"%2$s\");}}";
return String.format( template, classname, output );
}

Das Ganze können wir in eine Datei template.txt auslagern.

public class %1$s {
public static void main( String[] args ) {
System.out.println( "%2$s" );
}
}

Steht die Template-Datei im Klassenpfad, so greift das folgende generate(…) nun auf die Datei zurück und füllt die Platzhalter:

String generate( String classname, String output ) {
String template = new Scanner(getClass().getResourceAsStream( "template.txt" ))
.useDelimiter( "\\Z" ).next();
return String.format( template, classname, output );
}

Fortgeschrittener ist es, statt Positionen Namen einzusetzen. Dann ist auch String.format(…) nicht mehr nötig, sondern ein einfaches replace(…) ersetzt Platzhalter:

public class %CLASSNAME% {
public static void main( String[] args ) {
System.out.println( "%OUTPUT%" );
}
}

Und die generate(…)-Methode wird zu:

String generate( String classname, String output ) {
String template = new Scanner(getClass().getResourceAsStream( "template.txt" ))
.useDelimiter( "\\Z" ).next();
return template.replace( "%CLASSNAME%", classname).replace( "%OUTPUT%", output );
}

Das Ganze lässt sich noch generalisieren, indem statt der zwei Parameter eine Map an die Methode übergeben wird. Anstatt dann zwei feste replace(…)-Aufrufe zu setzen, läuft eine Schleife über die Map und führt das Ersetzen durch, sodass der Schlüssel – Name des Platzhalters – durch den assoziierten Wert ersetzt wird.

Anstatt so etwas selbst zu programmieren, können wir auf vorgefertigte Lösungen zurückgreifen. Es bieten sich Template-Engines an, die neben einer einfachen Ersetzung noch viel mehr können, etwa Variablendeklarationen, Berechnungen, Schleifen, Makros. Bekannte Vertreter sind:

Quellcodegenerierung über Modelle

Das Generieren von Text ist sehr einfach, doch prinzipiell mit Fehlern behaftet. Es kann sein, dass Bezeichner ungültig sind, Typen nicht passen oder Sonderzeichen eines Strings nicht richtig ausmaskiert sind. Das Herausschreiben von Text ist über die Vorlage praktisch, doch kann eine Bibliothek zum Generieren von Java-Quellcode helfen:

  • Eclipse Abstract Syntax Tree (AST) aus dem JDT: Eclipse bietet eine API, mit der sich Java-Programmcode aufbauen lässt.

  • CodeModel (http://codemodel.java.net/): Ein »Abfallprodukt« von JAXB, da JAXB mit xjc aus einem XML-Schema den Quellcode von JavaBeans generiert.

Nachdem der Syntax-Baum aufgebaut wurde, kann er in eine Textrepräsentation überführt werden. Die Eclipse-API ist angenehm, doch natürlich voll an den Eclipse-Compiler gebunden. Sprachänderungen der neuen Versionen kommen immer etwas verspätet. Die API von CodeModel ist nicht so komfortabel, und die Weiterentwicklung ist fragwürdig, da es lediglich als Unterprojekt von JAXB wirkt.

Fazit: Für ein einfaches Generieren von Quellcode sind die beiden APIs zu speziell. Templates sind sehr praktisch, wenn der Quellcode einem sehr festen Muster folgt, wie in unserem ersten Beispiel, in dem lediglich der Klassenname und ein String eingesetzt werden. Besser werden die API-Varianten, wenn der Quellcode weniger »frei« ist. Der größte Vorteil dieser Variante ist jedoch, dass sich der AST später einfacher modifizieren lässt. Auf der Basis von Quellcode ist das nicht möglich. Für Refactorings ist das optimal, aber um einfach Quellcode zu erzeugen, ist es etwas zu aufwändig.

 
Zum Seitenanfang

18.1.2Codetransformationen Zur vorigen ÜberschriftZur nächsten Überschrift

Bisher haben wir uns darauf konzentriert, Java-Quellcode über ein Java-Programm zu generieren. Es geht aber auch anders, etwa wenn der Ausgang ein XML-Dokument mit einer Beschreibung ist. Dann kann mittels XSLT eine Transformation von XML in ein Java-Programm stattfinden. Allerdings sind solche Transformationen nicht besonders angenehmen zu schreiben, doch möglich. XMLVM (http://www.xmlvm.org/) ist ein spannendes Projekt, das Java-Bytecode zunächst in XML abbildet. Eine anschließende XSLT-Transformation kann dann den Bytecode in ein anderes Zielformat überführen. Das Projekt ist aber nur ein Prototyp und seit Jahren inaktiv.

 
Zum Seitenanfang

18.1.3Erstellen von Java-Bytecode Zur vorigen ÜberschriftZur nächsten Überschrift

Ein Generator kann Java-Bytecode direkt erzeugen. Zwei bekannte APIs sind:

Beispiele finden sich auf den Webseiten. Insbesondere ASM ist beliebt, um Bytecode zur Laufzeit zu transformieren, um etwa bei der aspektorientierten Programmierung Aspekte, also Quellcode, einzuweben. Wir wollen das Erzeugen von Bytecode an dieser Stelle nicht weiter vertiefen und dem Compiler das Generieren von Bytecode überlassen.

 


Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Java SE 8 Standard-Bibliothek Java SE 8 Standard-Bibliothek
Jetzt Buch bestellen

 Buchempfehlungen
Zum Rheinwerk-Shop: Java ist auch eine Insel
Java ist auch eine Insel


Zum Rheinwerk-Shop: Professionell entwickeln mit Java EE 8
Professionell entwickeln mit Java EE 8


Zum Rheinwerk-Shop: Besser coden
Besser coden


Zum Rheinwerk-Shop: Entwurfsmuster
Entwurfsmuster


Zum Rheinwerk-Shop: IT-Projektmanagement
IT-Projektmanagement


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo

 
 


Copyright © Rheinwerk Verlag GmbH 2018. Original - https://www.rheinwerk-verlag.de/openbook/
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

 

 
 


29.09.2022 - Sitemap.xml