Was sind Java 8 Functional Interfaces?

Java 8 hat eine besonders interessante Funktionalität mitgebracht: Lambdas. Ein Lambda ist eine anonyme Funktion und kann etwa als Parameter einer Methode über- oder von einer Methode zurückgegeben werden.

Vor Java 8 hat man üblicherweise eine Klasse erstellt wenn man irgendein Stück Code verwenden wollte. Dies ist aber nicht immer nötig und generiert unnötigen Boilerplate Code.

Zu Lambdas komme ich aber ein anderes Mal. Jetzt geht es um die Functional Interfaces.

Kurze Übersicht

Functional Interfaces:

  1. Consumer = Akzeptiert ein Argument und gibt nichts zurück
  2. BiConsumer = Akzeptiert zwei Argumente, etwa von einer Hashmap, und gibt nichts zurück
  3. Supplier = Will kein Argument, aber gibt eines zurück
  4. Predicate = Gibt true oder false zurück
  5. Operator = Will den gleichen Typ als Input und Output

Wie sehen Functional Interfaces aus?

Ein Functional Interface hat üblicherweise eine @FunctionalInterface Annotation. Der Trick bei einem Functional Interface ist, dass diese eine einige abstrakte Methode besitzt. Die Implementation dieser Methode kann dann als Lambda geschehen.

Die neuen „Default“ Methoden, die auch mit Java 8 eingeführt wurden, sind übrigens nicht abstract und zählen daher auch nicht zu den Functional Interfaces.

Das Consumer Interface

Das Interface, was wohl am meisten verwendet wird, ist das Consumer Interface. Ein Consumer akzeptiert ein Argument und gibt nichts zurück. Mit dem Argument wird irgendetwas angestellt.

Zum Beispiel können wir eine Liste von Namen erstellen und dann jeden mit „Hallo“ begrüssen. Die Liste kann ganz einfach mit „Arrays.asList()“ gemacht werden (achtung: Das Arrays hat ein grosses ‚A‘).

List<String> namen = Arrays.asList("Hans", "Wurst", "Gerald");
namen.forEach(name -> System.out.println("Hallo " + name");

Bei der Methode forEach sehen wir schön, dass ein Consumer benötigt wird, welcher einen Parameter braucht, in diesem Fall einen String.

funcint1.PNG

Output:
Hallo Hans
Hallo Wurst
Hallo Gerald

Wenn wir die Klasse „java.util.function.Consumer“ öffnen sieht man am Anfang die Annotation @FunctionalInterface. Die abstrakte Methode ist in diesem Fall „accept“, da wir aber ein obercooles Lambda mit dem -> Pfeil verwendet haben, müssen wir diesen Namen gar nicht kennen.

funcint2.PNG

PS: Die Angabe zu „references | implementation“ ist übrigens vom Code Mining, das gehört nicht zu der Klasse, siehe Code Mining in Eclipse Photon

Das BiConsumer Interface

Ein BiConsumer ist ähnlich wie der Consumer, nur dass er zwei Argumente annimmt, etwa von einer HashMap. Wieder kann man bei der forEach Methode schauen, was benötigt wird, und sieht, dass die Methode ein BiConsumer benötigt.

funcint3.PNG

Map<String, Integer> personen = new HashMap<>();
personen.put("Hans", 20);
personen.put("Wurst", 22);
personen.put("Gerald", 34);
personen.forEach((name, alter) -> System.out.println(name + 
" ist " + alter + " Jahre alt"));

Output:
Wurst ist 22 Jahre alt
Gerald ist 34 Jahre alt
Hans ist 20 Jahre alt

Das Supplier Interface

Der Supplier ist das Gegenteil vom Consumer und absolut selbstlos. Er will überhaupt keinen Parameter, liefert aber dennoch einen Wert zurück.

Das Interface sieht so aus:

funcint4.PNG

Es braucht in der Methode get() keinen Parameter, gibt aber irgendetwas zurück.

Das einfachste Beispiel erstellt einfach einen Supplier, der irgendetwas zurückgibt, etwa in einem Lambda. Nicht vergessen, beim Supplier den Typ mit eckigen Klammern mitzugeben.

Supplier<String> halloSupplier = () -> new String("Hallo");
String halloString = halloSupplier.get();

Das Predicate Interface

Ein Predicate ist eine Funktion, die Inputwerte erhält und nach einer gewissen Logik ein true oder false zurückgeben, also einen Boolean.

Zum Beispiel kann man prüfen, ob ein Name mit „W“ anfängt.

List<String> personen = Arrays.asList("Hans", "Wurst", "Gerald");
List<String> namenMitW = namen.stream()
.filter(name -> name.startsWith("W")).collect(Collectors.toList());
namenMitW.forEach(name -> System.out.println(name));

Output: 
Wurst

Das Operator Interface

Das Operator Interface ist eine spezielle Funktion, die den gleichen Typ als Input wie auch als Output hat.

Die Methode „replaceAll“ etwa möchte einen UnaryOperator haben.

funcint5.PNG

Was übrigens auch neu ist, ist der Zugriff auf bestimmte Methoden mit dem Doppelpunkt, wie im Beispiel mit String::toUpperCase. (Wenn man in Eclipse „String::“ eingibt und dann CTRL + Leertaste drückt kommt eine Liste von Methoden und man kann einfach eine auswählen)

List<String> personen = Arrays.asList("Hans", "Wurst", "Gerald");
namen.replaceAll(String::toUpperCase);

Output: 
HANS
WURST
GERALD

Echt genial und gar nicht mal so kompliziert, wenn man mal ein paar einfache Beispiele gesehen hat, oder?

Aus einem XSD File Java Klassen erzeugen mittels JAXB Maven Plugin

JAXB heisst die Komponente, mit der man XML nach Java umwandeln („unmarshallen“) oder Java Klassen nach XML serialisieren kann („marshallen“).
Gegeben: Ein XSD File, welches eine bestimmte XML Struktur wiedergibt. Das soll in das aktuelle Projekt eingebunden werden, und aus dem XSD sollen von Maven automatisch Java Klassen generiert werden.

1. Java Klassen von Hand erstellen

Die Java Klassen können auch von Hand mit dem Tool xjs aus dem JDK erstellt werden. Dazu einfach das Tool per Commandline aufrufen, das XSD File und den Zielordner angeben und fertig.
  • CMD öffnen (PowerShell ist wie immer zu dumm dafür)
  • Zum JDK bin Ordner navigieren (etwa C:\Program Files\Java\jdk1.8.0_131\bin)
  • xjc.exe -d <Zielordner angeben> beispiel.xsd

2. Java Klassen mittels Maven Plugin erstellen lassen

2.1 JAXB als Maven Plugin eintragen

Hat man ein Maven Projekt mit einem pom.xml kann man das JAXB Plugin dort unter „project -> build -> plugins“ eintragen.
Das Plugin wird dann bei jedem Maven Goal „generate“ bzw. „generate-sources“ ausgeführt und die Java Klassen erstellt. Dies kann man auch manuell machen: Rechtsklick auf das pom.xml und dann „run as“ -> „Maven generate-sources“.
Das XSD kann man etwa in folgenden Ordner kopieren: src/main/resources/xsdordner/
Die Java Klassen werden dann unter „target/generated-sources/xjc“ erstellt und können im Projekt verwendet werden.
PS: Da die Java Klassen dann im Target Ordner lieben bzw. sowieso immer von Maven generiert werden müssen sie z.Bsp. nicht ins GIT committed werden.
Der BindingDirectory nennt den Ordner, wo das binding.xml liegt (siehe nächstes Kapitel).
            <plugin>
                <groupId>org.jvnet.jaxb2.maven2</groupId>
                <artifactId>maven-jaxb2-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <schemaIncludes>xsdordner/*.xsd</schemaIncludes>
                    <bindingDirectory>src/main/xjb</bindingDirectory>
                    <bindingIncludes>
                        <include>*.xml</include>
                    </bindingIncludes>
                </configuration>
            </plugin>
JAXB wird zum Beispiel auch im XSD definierte Enums entsprechend in den Java Klassen erstellen.
Wichtig: Die Java Klassen dürfen nie manuell bearbeitet werden, da Maven die Änderungen gleich wieder überschreiben würde!

2.2 Datei binding.xml erstellen

Das „binding.xml“ liegt etwa im Ordner „src/main/xjb“ und enthält verschiedene Angaben, wie das XSD in XML umgewandelt werden soll.
Zum Beispiel kann man mit „schemaBindings“ angeben, in welches Package die Klassen erstellt werden sollen. Mit „schemaLocation“ gibt man an, wo das XSD File liegt.
Ein Problem, was ich hatte, war, dass zwei Tags auf unterschiedlichen Ebenen gleich hiessen. Dies kann hier behoben werden, in dem man für ein Tag sagt, es soll einen anderen Klassennamen verwenden. Ansonsten gibt es ein riesiges Chaos, wenn zwei Tags etwa „Personen“ heissen.
Ich hatte etwa folgendes XSD:
<xs:element name="personen">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="freunde"> 
        <xs:complexType>
          <xs:element name="personen">
          </xs:element>
        </xs:complexType>
      </xs:element> 
    </xs:sequence>
  </xs:complexType>
</xs:element>
Das Problem in diesem XSD ist, dass zwei XS-Elemente den Namen „personen“ tragen. Um dem entgegenzuwirken, kann man im bindings.xml folgendes Binding erfassen:
    <jaxb:bindings schemaLocation="../resources/xsdordner/*.xsd" node="/xs:schema">
        <jaxb:schemaBindings>
            <jaxb:package name="ch.meinapp.meinpaket" />
        </jaxb:schemaBindings>
        <jaxb:bindings node="xs:element[@name='personen']">
            <jaxb:class name="PersonenRoot" />
        </jaxb:bindings>
    </jaxb:bindings>
Er macht dann einen XPath Abfrage auf das XML, um das Tag „personen“ zu finden. Dabei findet er nur das äussere Tag, für das inne wäre ja eine XPath Abfrage „personen -> freunde -> personen“ nötig. Für das äussere „personen“ Tag erstellt er dann eine Klasse namens „PersonenRoot“.

2.3 Adapter schreiben für ungünstige Klassen

In einem Attribut wurde ein Datum mit dem Typ „xsd:dateTime“ verwendet. JAXB hat daraus in der Java Klasse automatisch den Typ „XMLGregorianCalendar“ verwendet. XMLGregorianCalendar ist aber schon ziemlich alt und zudem nicht Threadsafe. Darum kann man im binding.xml mittels „globalBinding“ eine alternative Klasse angeben, die er verwenden soll.
    <jaxb:globalBindings>
        <xjc:javaType name="java.time.OffsetDateTime" 
xmlType="xs:dateTime" adapter="ch.meinapp.meinpaket.OffsetDateTimeAdapter" />
        <xjc:simple />
    </jaxb:globalBindings>
Der dazugehörige Adapter für dieses Beispiel mit OffsetDateTime sieht dann so aus:
public class OffsetDateTimeAdapter extends XmlAdapter<String, 
OffsetDateTime> {

private final DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;

public OffsetDateTime unmarshal(String lexicalRepresentation) {
    if (lexicalRepresentation == null) {
      return null;
    }
    return formatter.parse(lexicalRepresentation, OffsetDateTime::from);
}

public String marshal(OffsetDateTime dateTime) {
    if (dateTime == null) {
        return null;
    } 

    return formatter.format(dateTime);
    }
}

2.4 Das XSD so aufteilen, dass er mehrere Klassen macht

JAXB orientiert sich am XSD, um zu bestimmen, wieviele Java Klassen er erstellen soll. Das kann man durchaus beeinflussen.
Das folgende XSD würde eine Klasse „Personen“ erstellen mit „Freunde“ als anonyme innere Klasse:
<xs:sequence>
    <xs:element name="personen">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="freunde" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:sequence>
Wenn man das XSD aber folgendermassen aufsplittet, erstellt JAXB eine Klasse „Personen“ und eine Klasse „Freunde“. Man beachte, dass der complexType den gleichen Namen „personen“ wie das xs:element bekommen hat.
<xs:sequence>
     <xs:element name="personen" />
</xs:sequence>

<xs:complexType name="personen">
    <xs:sequence>
        <xs:element name="freunde" />
    </xs:sequence>
</xs:complexType>
Schlussendlich ist JAXB ein bischen Pain in the A** aber es funktioniert!

Code Mining in Eclipse Photon

Eclipse Photon ist im Juni 2018 erschienen und ist sehr viel schneller und besser benutzbar geworden (heisst es).

  • Umfangreicher Support von Java 10
  • Verbesserung des Dark Themes
  • Aktionsbasierte Labels auf Buttons
  • Operationen sind möglichst nicht blockierend
  • einfacherer Projektimport
  • Verbesserte Debug Perspektive
  • Vereinfachte Menüstrukturen
  • Schnelleres Feedback auf Useraktionen
  • Bessere „Tip of the Day“ Funktionalität
  • Debugger: When a method exit breakpoint is hit, the value being returned is now shown in the variables view.
  • The formatter profile preference page (Java > Code Style > Formatter > Edit…) has a
  • new look which makes it much easier to set preferences for formatting Java code.

Und vieles mehr, siehe https://www.eclipse.org/eclipse/news/4.8/M5/

Endlich hat Eclipse mit Code Mining auch eine ähnliche Funktion wie IntelliJ erhalten, um Zusatzinformationen im Code anzuzeigen. Das funktioniert dabei ähnlich wie CodeLens in Microsoft Visual Studio. Microsoft hat den Begriff „CodeLens“ aber markenrechtlich schützen lassen, darum heisst es in Eclipse CodeMining.

Installation

Sobald man Eclipse Photon installiert hat, kann man es unter „Help -> Install new Software“ installieren:

https://github.com/angelozerr/jdt-codemining

installation.PNG

Aktivieren in den Einstellungen

codemining.PNG

Referenzen, IF und FOR

Wichtig: Auf alle diese Angaben kann in Eclipse geklickt werden! 

  • Klassen und Methoden haben die auf sie zeigenden Referenzen angegeben. Ein Klick auf „references“ öffnet etwa die „Call hierarchy“.
  • Bei IF Statements wird bei der schliessenden Klammer angezeigt, zu welchem IF es gehört. Dasselbe bei FOR Statements. Ein Klick auf den Hinweis springt zum IF/FOR Statement.

code.PNG

Anzeigen der Werte beim Debuggen

Ähnlich wie im IntelliJ werden die Werte der Literale (Integers etc.) direkt angezeigt. Bei Instanzen wird leider nur die ID angezeigt, was nicht so viel bringt. Immerhin sieht man, ob die Variable null ist oder nicht.

Die Methodenaufrufe werden ergänzt durch den Typ der Variablen plus den Namen, den die Variable in der aufgerufenen Methode hat.

debug.PNG

jUnit Tests

Bei jUnit Tests sieht man an einem Icon den Status des Tests – Dieser wird grün oder rot, wenn man die Tests laufen lässt.

Das Laufenlassen ist nun ganz eifach möglich mit Klick auf „Run All / Debug All / Run / Debug“.

junit.PNG

Anzeigen von GIT Informationen

Auf Wunsch kann auch Informationen von GIT anzeigen – Etwa, wer eine Methode zum letzten Mal geändert hat und wann. Mir sind es aber zuviele Namen, weshalb ich diese Funktion deaktiviert habe.

Fazit

Echt genial! Ich kann es nur empfehlen.