Programmierung – Ausgewählte neue Funktionen in Java
Michael Goedicke
michael.goedicke@paluno.uni-due.de
Entwicklung von Java
▪ Die erste Java Version wurde von 1991 bis 1996 von James Gosling und weiteren entwickelt. Die Arbeiten fanden bei dem Unternehmen Sun Microsystems statt.
▪ Seit 1998 wird die Weiterentwicklung von Java über den Java Community Process (JCP) gesteuert. Neue Feature Anfragen werden mittels Java Specification Request (JSR) an den JCP gestellt.
▪ Das Java Development Kit (JDK) und die Java virtual machine (JVM) wurden 2006 / 2007 als Open Source Software veröffentlicht.
▪ Sun Microsystems wurde 2009 / 2010 von Oracle gekauft.
▪ Seit Java 9 wird zwischen Long Term Support (LTS) welcher mehrere Jahre gilt und normalen Versionen (support für halbes Jahr) unterschieden. Aktuelle Version ist Java 15 und die aktuelle LTS Version ist Java 11.
M. Goedicke – ProgrammierungWiSe2020/2021 2
Java 8 – Neue Möglichkeiten bei Interfaces
▪ Mit dem Schlüsselwort default wird bei einem Interface eine Methode mit Methodenrumpf ermöglicht.
▪ Die default Methode kann nur auf die anderen abstrakten und default Methoden des Interfaces sowie auf die finalen Konstanten zugreifen. Die default Methode kann auch vererbt und in abgeleiteten Interfaces überschrieben werden.
▪ Neben der default Methode kann auch eine statische Methode in einem Interfaces implementiert werden. Diese Methoden werden über den Interface Namen aufgerufen.
▪ Das Schlüsselwort default und static müssen vor dem Sichtbarkeitsmodifikator stehen.
M. Goedicke – ProgrammierungWiSe2020/2021 3
Java 8 – Interface default und static Beispiel
public interface Schneidbar {
double TOLERANZ_IN_PROZENT = 20.;
int getSchneidbareDicke();
default boolean istSchneidbar(int dickeBrett) { double maximaleDicke = getSchneidbareDicke() *
(1.+(TOLERANZ_IN_PROZENT / 100.)); return dickeBrett <= maximaleDicke;
}
static void nenneToleranzWert() { System.out.print("TOLERANZ_IN_PROZENT"+" Prozent");
} }
M. Goedicke – ProgrammierungWiSe2020/2021 4
Java 8 – Interface default und static Beispiel
public class Kreissaege implements Schneidbar{ private int schneidbareDicke;
public Kreissaege(int schneidbareDicke) {
this.schneidbareDicke = schneidbareDicke; }
public static void main(String[] args) { Kreissaege kreissaege = new Kreissaege(10); if(kreissaege.istSchneidbar(13)) {
System.out.println("Brett ist schneidbar"); }
else {
System.out.print("Brett ist dicker als "
+kreissaege.getSchneidbareDicke()
+" Millimenter inklusive Toleranz von: "); Schneidbar.nenneToleranzWert();
} }
public int getSchneidbareDicke() { return schneidbareDicke;
} }
M. Goedicke – ProgrammierungWiSe2020/2021 5
Java 9 - Modularisierung
▪ Dekomposition eines Systems in abgeschlossene, aber verbundene Module
▪ Metadaten beschreiben das Modul und die Verbindung zu anderen Modulen
▪ Module erleichtern die Verstehbarkeit eines Systems, indem Implementierungsdetails
hinter Schnittstellen strikt verborgen werden
▪ Kohäsion: Sagt aus, wie eine Programmeinheit eine einzelne Aufgabe in sich selbst
erfüllt
▪ Starke Kohäsion bedeutet, dass eine Programmeinheit für eine abgegrenzte Aufgabe zuständig
ist
▪ Abhängigkeit von anderen Programmeinheiten wird dadurch reduziert
▪ Änderungen an Programmeinheiten ziehen seltener Änderungen an anderen Einheiten nach
sich
▪ Einheit kann besser verstanden werden, ohne viele andere Einheiten ebenfalls verstehen zu
müssen
▪ Kopplung: Grad der Abhängigkeit einer Programmeinheit zu anderen Einheiten
▪ Lose Kopplung = kleine Schnittstelle zu anderen Einheiten
▪ Auch hier bestehen die Vorteile einer starken Kohäsion
▪ Verwendung von Modulen fördert eine lose Kopplung bei hoher Kohäsion der Module
M. Goedicke – Programmierung WiSe2020/2021 6
Modularisierung
Schwache Kohäsion + starke Kopplung
▪ Kernanforderungen an Module ▪ Strikte Kapselung
Hohe Kohäsion + lose Kopplung
▪ Es wird vor anderen Modulen verborgen, was nicht genutzt werden soll -> ungewollte Kopplung wird vermieden
▪ Wohldefinierte Schnittstellen
▪ Schnittstellen, die sich aufgrund schlechten Designs oft ändern,
können anderen Code unbrauchbar machen ▪ Explizite Abhängigkeiten
▪ Notwendig, um zuverlässige Konfiguration von Modulen zu erstellen M. Goedicke – Programmierung WiSe2020/2021 7
Modularisierung vor Java 9
▪ Große Java-Projekte verwenden oft viele Bibliotheken
▪ Verstehbarkeit von Quelltext steht im Zusammenhang mit der Menge
an verwendbaren Methoden und Klassen
▪ Trotz Paketen kann Modularisierung in Java schwer sein:
▪ Sichtbarkeit von Klassen
▪ Wird definiert durch Pakete und Zugriffsmodifizierer
▪ Wenn innerhalb eines Projektes Klassen paketübergreifend genutzt
werden müssen, sind diese zwingend auch global sichtbar ▪ Definition von Abhängigkeiten
▪ Abhängigkeiten von Klassen über import-Statement
▪ Wird nur zur Compilezeit geprüft
▪ Ob alle Abhängigkeiten zur Laufzeit im Classpath vorhanden sind,
wird vorher nicht sichergestellt
M. Goedicke – Programmierung WiSe2020/2021 8
Modularisierung mit Java 9
▪ Mit Projekt „Jigsaw“ wurde ein echtes Modulsystem in Java eingeführt
▪ Die JRE wird in Module aufgeteilt
▪ Eigene Anwendungen können mit den gleichen Mechanismen
modularisiert werden
▪ Erstellung eines Modulgraphen möglich
▪ Modulepath ersetzt den Classpath
myModule
▪ JDK 9 führt neues Tool ein, um eine abgespeckte JRE zu erstellen, die nur die benötigten Module enthält
M. Goedicke – Programmierung WiSe2020/2021 9
Typinferenz für lokale Variablen mit Java 10
▪ Typinferenz, auch als Typableitung bezeichnet, lässt den Compiler den korrekten Typ der Variable ableiten
▪ Mittels var kann ab Java 10 bei lokalen Variablen der Typ durch den Compiler abgeleitet werden:
▪ var diesIstEinString = “Hallo”;
▪ var soll die Lesbarkeit des Codes verbessern
▪ Variable muss mit eindeutigem Typ initialisiert werden
▪ Ausdrücke, die keinen eindeutigen Typ haben, können nicht mittels
var ausgerückt werden. (Poly-Expressions)
▪ Array-Initialisierer, Lambda-Ausdrücke und Methodenreferenzen sind
Beispiele für Poly-Expressions.
▪ Vorsicht bei Generics (Diamantoperator <> ohne Typ vermeiden) und
Zahlliteralen (wenn nicht explizit definiert wird var zu int), hier sollte
var mit Bedacht genutzt werden!
▪ Vorsicht: Code-Review-Werkzeuge können mit var meist nicht
umgehen
M. Goedicke – ProgrammierungWiSe2020/2021 10
Typinferenz für lokale Variablen mit Java 10
▪ Bessere Lesbarkeit des Code bereits bei kurzen Typbezeichnern
var paluno = new URL(“https://paluno.uni-due.de”); var connection = paluno.openConnection();
var reader = new BufferedReader(
new InputStreamReader(connection.getInputStream())); readFirstLines(reader);
▪ Zuvor:
URL paluno = new URL(“https://paluno.uni-due.de”); URLConnection connection = paluno.openConnection(); Reader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream())); readFirstLines(reader);
M. Goedicke – ProgrammierungWiSe2020/2021 11
Typinferenz für lokale Variablen mit Java 10
▪ Beispiel mit sehr unterschiedlich langen Typbezeichnern:
// ohne var
No no = new No();
AmountIncrease
new HorizontalLinePositionConnection(); Variable variable = new Constant(5); List
// mit var
var no = new No();
var more = new BigDecimalAmountIncrease();
var jumping = new HorizontalLinePositionConnection(); var variable = new Constant(5);
var names = List.of(“Max”, “Maria”);
M. Goedicke – ProgrammierungWiSe2020/2021 12
Typinferenz für lokale Variablen mit Java 10
▪ Um die positiven Effekt von var effektiv zu nutzen sollten folgende
Richtlinien beachtet werden:
▪ Gute Variablennamen wählen und auf aussagekräftige Initialisierung
achten.
▪ Geltungsbereich überschaubar halten.
▪ Die Chance für mehr Zwischenvariablen nutzen.
▪ Vorsicht bei Refactorings, wenn var zum Einsatz kommt
▪ DieVerwendung von var im Entwicklungsteam absprechen und alle
verwendeten Entwicklungswerkzeuge in die Überlegungen einbeziehen. ▪ var sollte nicht verwendet werden, wenn der Typ nicht klar erkennbar
ist:
▪ var one = Arrays.asList(1, 2, 3).stream().findFirst().orElseGet(null);
M. Goedicke – ProgrammierungWiSe2020/2021 13
Erweitertes Switch mit Java 12 & 13
▪ Switch wurde um viele Funktionsmöglichkeiten erweitert
▪ Ein case kann mehrere Fälle behandeln
▪ Pfeilsyntax verändert bekanntes Verhalten und ist analog zur
Lambdasyntax zu sehen. ▪ Kein fall-through
▪ Anweisungsblöcke
▪ Neben dem switch-statement gibt es jetzt switch-expressions ▪ Switch-expression weißt einer Variablen einen Wert zu
▪ Semikolon am Ende zeigt den Status als Anweisung
▪ „yield“ gibt den Wert eines Falls zurück.
M. Goedicke – ProgrammierungWiSe2020/2021 14
Erweitertes Switch mit Java 12 & 13
▪ Ein case kann mehrere Fälle behandeln:
int x=5;
switch (x) { case 1,2,3,5,7,9:
System.out.println(“ungerade”);
break;
default:
System.out.println(“gerade”);
break; }
M. Goedicke – ProgrammierungWiSe2020/2021 15
Erweitertes Switch mit Java 12 & 13
▪ Pfeilsyntax lässt kein fall-through mehr zu. Es wird immer nur der zugehörige Fall behandelt.
▪ Mittels Klammerung sind Anweisungsblöcke möglich
int x = 5;
switch (x) {
case 1, 2, 3, 5, 7, 9 -> System.out.println(“ungerade”); default -> {
System.out.println(“gerade”);
System.out.println(”Zweite Anweisung”); }
}
M. Goedicke – ProgrammierungWiSe2020/2021 16
Switch-Anweisung mit Java 12 & 13
▪ Durch switch als Anweisung kann einer Variablen direkt das Ergebnis des zutreffenden switch-Falls zugewiesen werden
▪ Semikolon am Ende zeigt den Status als Anweisung
▪ yield gibt den Wert eines Falls zurück. return ist nicht erlaubt. ▪ Alle Fälle müssen abgedeckt sein, sonst Compiler-Fehler
▪ default nicht notwendig, wenn alle Fälle abgedeckt sind. Beispielsweise alle Fälle eines Enums oder true/false für boolean
String name = “Jack”;
int ergebnis = switch (name) { case “Jack”:
yield 3; case “”:
yield 0; case “Testat”:
yield 100; default:
throw new IllegalArgumentException(„Falscher Wert:” + name); };
M. Goedicke – ProgrammierungWiSe2020/2021 17
Zusammenfassung Switch mit Java 12 & 13
▪ Ein case kann mehrere kommagetrennte Werte abdecken
▪ Die neue Pfeilsyntax folgt der Lambdasyntax ▪ Single-line statement oder Codeblock
▪ Verhindert fall-through zum nächsten case
▪ Beide Erweiterungen für switch-Ausdruck und –Anweisung nutzbar ▪ Switch jetzt auch als Anweisung
▪ Berechnet einen Wert, der zugewiesen oder weitergegeben wird
▪ Yield gibt den Wert eines Blocks zurück
▪ Return ist in Anweisung nicht erlaubt
▪ Compiler prüft Fälle auf Vollständigkeit.
▪ Switch-Anweisung ist Poly-Expression. D.h. var kann genutzt werden. Ist Ergebnistyp bekannt müssen alle Fälle diesem Typ genügen. Ansonsten wird spezifischster Typ gewählt, der alle Falltypen abdecken kann.
M. Goedicke – ProgrammierungWiSe2020/2021 18