Innovative Sprachfunktionen für Java – Teil 3: Typsicherheit bewahren

Manifold: Typsicherer Reflexionscode mit @Jailbreak

Scott McKinney

© Shutterstock /Martins Vanags

Fällt Euch die Arbeit mit Reflexionscode schwer? Der Reflexionscode an sich ist nicht typsicher, weshalb es später zu Problemen kommen kann. Doch keine Panik, es gibt eine Alternative! Mit @Jailbreak aus dem Manifold-Projekt kann man die Typsicherheit bewahren und sich der Effizienz seines Codes sicher sein.

In der vorliegenden Artikelserie geht es um Manifold, eine brandneue Technologie zur Erweiterung von Java. In Teil 1 wurde das Type Manifold API vorgestellt, eine leistungsstarke Alternative zur gewöhnlichen Code-Generierung. Im zweiten Teil ging es um die sog. Extension Classes (Erweiterungsklassen), ein innovatives Feature, durch das eine Klasse mit eigenen Methoden, Interfaces und anderen Features ergänzt werden kann, ohne auf Unterklassen zurückgreifen oder Änderungen an der Originalklasse vornehmen zu müssen. Im dritten Teil behandeln wir die Annotation @Jailbreak.

Wer hat schon mal Felder und Methoden overexposed, nur um von Tests aus, auf diese zugreifen zu können? Wer hat schon mal Reflexionscode geschrieben, um auf private Klassenmitglieder zuzugreifen?

Damit ist nun Schluss, denn mit @Jailbreak aus dem Manifold-Projekt lassen sich Integrität und Typsicherheit bewahren.

  
@Jailbreak MyWidget widget = new MyWidget();
String value = widget.privateField; // type-safely access private fields and methods

Das Problem der Relfexion

Normalerweise wird abgeraten, Reflexion zu nutzen, um auf sonst unzugängliche Klassenmitglieder zuzugreifen oder sie zu modifizieren. Nun, das hängt von den Alternativen ab. Es gibt Situationen, in denen die Reflexion das kleinere der zwei Übel darstellt oder für den Kundengewinn bzw. ihren Verlust entscheidend ist. Tatsache ist, dass die Reflexion eine unverzichtbare JVM-Funktion ist, von der wir alle profitieren. Allerdings ist es auch ein gutes Zeichen, wenn einem bei deren Einsatz nicht ganz wohl ist. Reflexionscode ist eine unangenehme Angelegenheit, da er sich der Typsicherheit vollkommen entzieht.

Nehmen wir einmal an, dass man Information zum Brand von myWidget benötigt:

 public class MyWidget implements Widget {
  private String brand;
  ...
}

Da brand privat und MyWidget nicht die Klasse ist, die man ändern muss, muss man auf Reflexion zurückgreifen:

MyWidget myWidget = (MyWidget) widget;
...
Field field = MyWidget.class.getDeclaredField("brand");
field.setAccessible(true);
String brand = (String) field.getValue(myWidget);

Der Einsatz von Strings und Casting sorgt dafür, dass die Reflexion schwer zu lesen und fehleranfällig beim Schreiben ist. Noch schlimmer ist, dass sie sich der Typsicherheit entzieht. Der Compiler kann weder unseren Zugriff auf das brand-Feld, noch dessen String-Typ überprüfen. Wenn MyWidget 2.0 etwas davon verändert, wird unser Build den Bruch nicht erkennen. Das eigentliche Problem mit Reflexionscode ist also, dass er statisch nicht nachweisbar ist.

@Jailbreak

Angesichts der Schwierigkeiten beim Lesen und Schreiben von Reflexionscode sowie seiner mangelnden Typsicherheit, sollte es einen besseren Weg geben.

Warum nicht ein Sprachkonstrukt bereitstellen, um die Absicht zu deklarieren, die Kapselung zu untergraben und dann die Stärken des Compilers zu nutzen, um unseren Code zu verifizieren? Genau das ist es, was man mit der @Jailbreak Annotation erreicht:

@Jailbreak MyWidget myWidget = (MyWidget) widget;
...
String brand = myWidget.brand;

Wie wir hier sehen können, kann man mit @Jailbreak auf ansonsten unzugängliche Mitglieder von MyWidgetf, über die Variable myWidget, zugreifen.

@Jailbreak verringert die Probleme, die durch Reflexion entstehen. Es kann angewendet werden, um den Komfort und die Typsicherheit des Java-Compilers zu nutzen und mit Manifold, zuverlässigen, effizienten Reflexionscode zu generieren.

Grundlegende Anwendung

Dies ist ein Beispiel für die grundlegende Verwendung:

@Jailbreak Foo foo = new Foo(1);
foo.privateMethod();
foo.privateMethod("hey");
foo._privateField = 88;
public class Foo {
  private final int _privateField;
   
  public Foo(int value) {
    _privateField = value;
  }
   
  private String privateMethod() {
    return "hi";
  }
   
  private String privateMethod(String param) {
    return param;
  }
}

Verwendung der jailbreak()-Erweiterung

Ähnlich wie bei @Jailbreak kann man die Erweiterungsmethode jailbreak() von jeder beliebigen Expression aus aufrufen, um einen typsicheren Zugriff auf private Felder, Methoden und Typen zu erhalten.

Foo foo = new Foo();
foo.jailbreak().privateMethodOnFoo();

Diese Methode ist besonders praktisch, wenn man eine Kette von Member-Zugriffsausdrücken hat und diese prägnant verwenden möchte.

something.foo().jailbreak().bar.jailbreak().baz = value;

Anmerkung: Reflection-Code ist auch schrecklich ineffizient, wenn er nicht ordnungsgemäß zwischengespeichert und abgerufen wird. Manifold behebt auch all das.

IntelliJ IDEA

Verwendet @Jailbreak mit dem Manifold Plugin für IntelliJ IDEA. Das Plugin bietet umfassende Unterstützung für @Jailbreak, einschließlich Codevervollständigung, Navigation, Nutzungssuche, etc.

Mehr über das Manifold-Projekt kann man auf GitHub erfahren.

Sie suchen nach mehr Informationen über Manifold? Dann lesen Sie unsere zweiteilige Serie von Scott McKinney über innovative Sprachfunktionen für Java.
Teil 1: Manifold: Die Neuerfindung von Codegeneratoren
Teil 2: Manifold: Erweiterungsmethoden für Java
Geschrieben von
Scott McKinney
Scott McKinney
Scott McKinney is the founder and principle engineer at Manifold Systems. Previously, he was a staff engineer at Guidewire where he designed and created Gosu. He currently pounds code by the truckload while listening to way too much retro synthwave.
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
4000
  Subscribe  
Benachrichtige mich zu: