Interview mit Rafael Winterhalter

„Das Typensystem von Java ist eines der pragmatischsten, das ich kenne“

Hartmut Schlosser

Rafael Winterhalter

Java verfügt über eine statische Typisierung – mit allen Vor- und Nachteilen, die mit einem solchen System verbunden sind. Während statische Typen dabei helfen, potenzielle Fehlerquellen auszuschließen, verhindern sie auch die Flexibilität dynamischer Sprachen. Im Gespräch mit JAX-Speaker Rafael Winterhalter gehen wir den Eigenheiten des Java-Typensystems auf den Grund und klären, wie auch in Java dynamische Effekte durch Codegenerierung zur Laufzeit möglich sind.

JAXenter: Auf GitHub bezeichnest du dich als „Software Consultant who likes static types“. Das statische Typensystem in Java scheint es dir also angetan zu haben. So sehr, dass du das Projekt Byte Buddy gestartet hast, um Java dynamischer zu machen Was findest du am Java-Typensystem gut? Und was stört dich daran?

Rafael Winterhalter: Grundsätzlich empfinde ich Javas Typensystem als eines der pragmatisch besten, das ich kenne. Durch die durchgehend statische Typisierung kann man meistens sehr einfach auch durch unbekannte Java-Anwendungen navigieren. Das macht es sehr einfach, sich mit einem Programm bekannt zu machen. Das hilft oft beim Onboarding neuer Entwickler, was meiner Meinung nach Java als Enterprise-Sprache erfolgreich macht.

Die Explizität macht Java-Programme sehr ausdrucksstark.

Gleichzeitig ist Javas Typensystem einfach zu lesen, gerade weil Javacode teils ein bisschen weitschweifiger ist als andere Sprachen. Diese Explizität macht Java-Programme nach meiner Erfahrung oft sehr ausdrucksstark.

Kritik kann man vielleicht an der mangelnden Flexibilität des Typensystems üben. Gerade generische Typen könnten natürlich in einer erweiterten Form umfangreichere Typensicherheit zulassen. Aber diese zusätzlichen Mittel würden mit einem hohen Preis kommen, gerade für beginnende Javaentwickler, denen viel Code nicht mehr zugänglich wäre.

JAXenter: Auf der JAX hältst du eine Session zum Thema „Making Java more dynamic„. Darin bringst du die Möglichkeit der Code-Generierung zur Laufzeit ins Spiel. Wie genau funktioniert das?

Rafael Winterhalter: Die Java Virtual Machine führt ja eigentlich keine Java-Programme aus, sondern verarbeitet so genannten Java Bytecode. Dieser ist ein wenig technischer aufgebaut als die Sprache Java, ist in seiner Struktur aber relativ einfach. Byte Buddy generiert dabei zur Laufzeit Java-Programme mit einem API statt einen Compiler anzubieten. Diese Java-Klassen werden dann ins laufende Programm geladen, um dessen Verhalten zu verändern.

Dies ist manchmal notwendig, um zum Beispiel Proxys zu generieren. Diese lassen sich mit dem Compiler nur schwer erzeugen, unter anderem wegen Javas Typensystem. Weiterhin ist es möglich, existierenden Code mittels so genannter Java-Agenten umzuschreiben. Das ermöglicht es, Anwendungen zu erweitern, ohne diese neu zu kompilieren. Dieses Verfahren nutzen beispielsweise APM-Tools, um Metriken zur Laufzeit zu sammeln.

Ein „Hello World!“ mit dem Byte Buddy

Jede erzeugte Java-Klasse beginnt mit einer Instanz der Byte-Buddy-Klasse, die eine Konfiguration für die Erstellung neuer Typen repräsentiert:

1. Class< dynamicType = new ByteBuddy()
2.     .subclass(Object.class)
3.     .method(ElementMatchers.named("toString"))
4.     .intercept(FixedValue.value("Hello World!"))
5.     .make()
6.     .load(getClass().getClassLoader())
7.     .getLoaded();
8.    
9.   assertThat(dynamicType.newInstance().toString(), is("Hello World!"));

Die Default-ByteBuddy-Konfiguration, die im oberen Beispiel genutzt wird, erzeugt eine Java-Klasse in der neuesten Version des Klassen-File-Formats, das von der ausführenden Java Virtual Machine verstanden wird. Wie im Beispielcode ersichtlich, erweitert der erzeugte Type die Object-Klasse und überschreibt ihre toString-Methode, die nun den neuen Wert Hello Word! zurückgeben sollte. Die zu überschreibende Methode wird durch einen so genannten ElementMatcher identifiziert.

Im Beispiel kommt ein vordefinierter Element-Matcher named(String) zum Einsatz, der Methoden über ihre genauen Namen identifiziert. Byte Buddy bietet zahlreiche vordefinierten und gut getesteten Matcher, die in der ElementMatchers-Klasse gesammelt vorliegen und einfach miteinander kombiniert werden können. Das Erstellen eigener Matcher ist aber ebenso möglich, indem man einfach das (funktionale) ElementMatcher-Interface implementiert.

Um die toString-Methode zu implementieren, definiert die FixedValue-Klasse einen konstanten Rückgabewert für die überschriebene Methode. Die Definition eines konstanten Wertes ist indes nur ein Beispiel für die verschiedenen Methoden-Interceptors, die Byte Buddy mitbringt. Durch die Implementierung des Implementation-Interface kann eine Methode auch durch eigenen Bytecode definiert werden.

Schließlich wird die beschriebene Java-Klasse erzeugt und danach in die Java Virtual Machine geladen. Zu diesem Zweck ist ein Ziel-Class-Loader nötig, der von der umgebenden Klasse abgeleitet wird. Zu guter Letzt können wir uns das Ergebnis anschauen, indem wir die toString-Methode auf einer Instanz der erzeugten Klasse aufrufen und den Rückgabewert suchen, der den konstanten Wert aufweisen sollte, den wir erwarten.

Byte Buddy ist natürlich zu weitaus komplexeren Klassengenerierungen fähig. Zudem ist Byte Buddy nicht auf das Erzeugen von Unterklassen begrenzt, sondern kann auch existierenden Code transformieren. Über ein API können so genannte Java-Agents definiert werden, die Code-Transformierungen zur Laufzeit einer beliebigen Java-Anwendung ermöglichen.

Lesen Sie auch: Wer hat das beste Typensystem? JavaScript!

JAXenter: Kannst du einmal ein Beispiel nennen, wie durch Codegenerierung zur Laufzeit besser modularisierte Anwendungen möglich werden?

Rafael Winterhalter: Beispiele bieten sich in jeder Enterprise-Anwendung, die Codegenerierung nutzt. So implementiert etwa Spring seine AOP-Aspekte mithilfe von Klassen, die zur Laufzeit generiert werden. Ist eine Methode beispielsweise als transaktional markiert, erstellt Spring eine Unterklasse, die diese Logik in einer überschriebenen Methode bereitstellt. Ohne eine solche Möglichkeit könnte Spring diese Funktion nur anbieten, wenn ein Nutzer explizit eine Methode in Spring aufruft, was diesen Code stärker an Spring bindet.

Ich hoffe, dass das VM-Team seinen konservativen Kurs beibehält.

JAXenter: Wie erwähnt hast du mit dem Byte-Buddy-Projekt selbst eine Codegenerierungs-Library am Start. Wie unterscheidet sich Byte Buddy zu anderen Libraries wie ASM, Javassist oder cglib?

Rafael Winterhalter: Viele Bibliotheken zur Codegenerierung erfordern ein gewisses Verständnis von Java-Bytecode und lassen viele Fehler zu, wenn die APIs nicht richtig bedient werden. Das gilt besonders für ASM. Byte Buddy hat das Ziel, erfahrenen Java-Programmierern das Arbeiten mit Bytecode leicht zu machen, indem es sich an die Syntax von Java-Programmen anlehnt. Als Entwickler soll man nicht über Bytecode nachdenken müssen und Byte Buddy nur beschreiben, welchen Code man generieren möchte.

JAXenter: Welche Änderungen am Java-Typensystem oder der JVM würdest du dir für zukünftige Java-Versionen wünschen?

Rafael Winterhalter: So wenige wie möglich. Javas Typensystem ist bereits sehr ausdrucksstark. Auch weniger erfahrene Entwickler werden schnell produktiv. Ich hoffe, dass diese Anforderung nicht nach oben verschoben wird. Natürlich machen die neuen funktionalen Züge von Java gewisse Änderungen im Typensystem notwendig, aber insgesamt hoffe ich, dass das VM-Team seinen konservativen Kurs beibehält.

Rafael Winterhalter arbeitet als Software Consultant in Oslo (Norwegen). Er ist Befürworter eines statischen Typensystems und ein JVM-Enthusiast mit einem besonderen Interesse an Code Instrumentation, Concurrency und Funktionaler Programmierung. Rafael blog über Softwareentwicklung, präsentiert regelmäßig auf Konferenzen und wurde zum Java Rock Star erklärt. Wenn er in seiner Freizeit codet, wirkt er an einer großen Auswahl an Open-Source-Projekten mit. Er arbeitet oft an Byte Buddy, einer Library für einfache Laufzeit-Codegenerierung für die JVM. Für seine Arbeit erhielt Rafael einen Duke’s Choice Award und wurde zum Java Champion gewählt.
Geschrieben von
Hartmut Schlosser
Hartmut Schlosser
Hartmut Schlosser ist Redakteur und Online-Koordinator bei Software & Support Media. Seine Spezialgebiete liegen bei Java-Enterprise-Technologien, JavaFX, Eclipse und DevOps. Vor seiner Tätigkeit bei S & S Media studierte er Musik, Informatik, französische Philologie und Ethnologie.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.