Types with no names

Typinferenz für lokale Variablen in jOOQ nutzen

Lukas Eder

© Shutterstock.com / TypoArt BS

Nach dem erfolgreichen Release des JDK 9 ist es aktuell schon möglich, sich die Early-Access-Version des JDK 10 anzuschauen. Bislang gibt es noch eine recht übersichtliche Anzahl an JEPs für die nächste Version des JDKs, für Entwickler ist dabei womöglich JEP 286 am interessantesten: Typinferenz für lokale Variablen.

Dieser Artikel wurde zuerst auf jooq.org veröffentlicht, einem Blog, das sich mit allen Fragen rund um Open Source, Java and Software-Entwicklung aus der Perspektive von jOOQ befasst.

Über das neue Feature, das voraussichtlich Teil des JDK 10 sein wird, haben wir bereits gebloggt. Nun kann man entweder die Beschreibung des neuen Features im JEP selbst lesen, oder einfach die neueste Early-Access-Version herunterladen und damit herumspielen. Eines der besten Dinge der Typinferenz für lokale Variablen ist die Tatsache, dass wir nun nicht benennbare Typen verwenden. So ist Folgendes nun möglich:

In diesem Beispiel ist der Typ von o nicht benennbar, wir können ihm also keinen Namen geben – wir könnten ihn überflüssigerweise allerdings einer Objektvariable zuordnen. Durch das neue Keyword var kann den Typ „erfassen“, wie ich es nenne, und ihn lokal nutzbar machen. Auf diese Funktionalität kann man bereits vor Java 10 zurückgreifen, indem man Methoden oder Attributreferenzen verkettet.

Ein eher selten genutztes Feature ist die Verwendung von Methoden in anonymen Klassen, die nicht die Methode eines Super-Types überschreiben oder implementieren. Diese sind nur in einem sehr engen Rahmen verfügbar. Vor Java 10 konnte man entweder m() oder n() auf einer solchen Klasse aufrufen, mit der folgenden Syntax nicht aber beide:

(new Object() {
    void m() { 
        System.out.println("m"); 
    }
    void n() { 
        System.out.println("n"); 
    }
}).m();
 
// Now, how to call n()?

Dies ist im Prinzip nichts anderes, als die Verkettung von Methoden, bei der der Aufruf von m() an den Konstruktor-Call gekettet ist.

Die Sprachfunktion zum Hinzufügen von Methoden zu anonymen Klassen war nicht sehr nützlich. Von der „Außenseite“ der anonymen Klasse konnte nur eine Methode aufgerufen werden, da die Instanzreferenz schnell weg ist. Mit Java 10 können wir den gesamten Ausdruck einer lokalen Variable zuweisen, ohne den anonymen Typ zu verlieren.

Randnotiz: In Java gab es schon immer eine komische Hassliebe zur strukturellen Typisierung, da sie nur nominell eine typisierte Sprache ist. Wie wir nun aber sehen, hat sich eine neue Art der strukturellen Typisierung in die Sprache geschlichen. Cool!

Was bedeutet dies für jOOQ?

jOOQ hat ein paar sehr coole Types, das sieht man bereits am API:

Letztlich bekommt man verschiedene Record[N]<T1, T2, …, T[N]>-Typen, je nachdem wie viele verschiedene Spalten das Projekt in der SELECT-Anweisung haben soll.

for (Record3<String, String, String> r : using(con)
        .select(c.TABLE_SCHEMA, c.TABLE_NAME, c.COLUMN_NAME)
        .from(c))
  System.out.println(
    r.value1() + "." + r.value2() + "." + r.value3());

Was schön ist, ist die Tatsache, dass es Typsicherheit auf dem Level der Datensätze gibt: Man weiß also zum Beispiel, dass der Datensatz 3 Spalten hat und alle Inhalte vom Typ String sind. Der Haken an der Sache ist, dass man natürlich die Types tatsächlich niederschreiben muss, wenn man davon profitieren will, was auf Dauer sehr anstrengend werden kann. Das gilt insbesondere dann, wenn 16 oder mehr Spalten gewählt werden.

Java 10 wird dies wohl ändern. Dann sollte es möglich sein, einfach Folgendes zu schreiben:

for (var r : using(con)
        .select(c.TABLE_SCHEMA, c.TABLE_NAME, c.COLUMN_NAME)
        .from(c))
  System.out.println(
    r.value1() + "." + r.value2() + "." + r.value3());

Im Beispiel verwenden wir das Keyword var (oder auch final var) um eine Loop-Variable zu erstellen – und diese wird dann sogar typsicher sein. Es wäre bspw. nicht möglich, r.value4() auf ihr aufzurufen:

jshell> for (var r : using(con)
   ...>         .select(c.TABLE_SCHEMA, c.TABLE_NAME, c.COLUMN_NAME)
   ...>         .from(c))
   ...>   System.out.println(r.value1() + "." + r.value2() + "." + r.value4());
|  Error:
|  cannot find symbol
|    symbol:   method value4()
|      System.out.println(r.value1() + "." + r.value2() + "." + r.value4());
|                                                               ^------^

Das ist natürlich kein wirklicher Game Changer. Entwickler aus den Bereichen Scala und Kotlin werden sich aber sicher darüber freuen, diese Möglichkeiten nun auch in Java zu haben.

Das Ganze ist im Übrigen auch nicht nur für jOOQ nützlich, ganz im Gegenteil. Man kann es auch nutzen, um dynamisches SQL zu erstellen, etwa so:

// Create a subquery listing all tables called TABLES in any schema
var subq = select(t.TABLE_SCHEMA, t.TABLE_NAME)
          .from(t)
          .where(t.TABLE_NAME.eq("TABLES"));
 
// Create a predicate that uses the above subquery:
var pred = row(c.TABLE_SCHEMA, c.TABLE_NAME).in(subq);
 
// use the above predicate in an actual query
var q = using(con).selectFrom(c).where(pred);

Sollte die Typinferenz für lokale Variablen im JDK 10 enthalten sein, wird dies ein sehr nützliches Release für jOOQ-Nutzer sein.

Lesen Sie auch:

10 Tipps: So werden Sie zum richtig guten Programmierer

Verwandte Themen:

Geschrieben von
Lukas Eder
Lukas Eder
Lukas Eder ist leidenschaftlicher Java- und SQL-Entwickler. Er ist Gründer und Leiter der Forschungs- und Entwicklungsabteilung der Data Geekery GmbH, dem Unternehmen hinter jOOQ - die einfachste Art, um SQL in Java zu schreiben.
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu: