Post on 15-Jul-2020
Javakurs für Fortgeschrittene
Einheit 11: Java 8 & JUnit
Lorenz Schauer
Lehrstuhl für Mobile und Verteilte Systeme
Heutige Agenda
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 2
Lernziele Sehen, was Java 8 Neues bringt Funktionale Sprachelemente und Streams kennenlernen Kurze praktische Einführung in Tests mit dem JUnit Framework
Neurungen in Java 8
Motivation
Lambda-Ausdrücke
Default-Methoden
Streams
Praxis:
Arbeiten mit Streams und Lambda
JUnit Tests
Einführung in Tests mit JUnit
Motivation
Syntax
JUnit-Tests unter Eclipse
Geschichtliches:
Seit Java 5 Diskussionen um Spracherweiterung für funktionaleProgrammierung: „Closure-Debatte“
Seit 2009: Notwendigkeit für Lambda-ähnliche Konstrukte
Standard in anderen Programmiersprachen
Einfache Unterstützung für Parallelisierung gesucht
Aus Closures entwickelten sich:
Lambda-Ausdrücke
Methoden- und Konstruktorreferenzen
Fester Bestandteil seit Java 8
Motivation
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 3
Lambda-Ausdrücke
Wir erinnern uns (letzte Stunde):
Ein Objekt, welches Iterable implementiert kann das Ziel einer for-each-Schleife sein.
Alle Collection-Klassen implementieren Iterable und damit kann eine for-each-Schleife immer über diese Sammlungen laufen:
Nachteil: Nur sequentielle Verarbeitung der Elemente einer Collection Keine Trennung von Iteration und Verarbeitung
Keine Parallelisierung!
Daher: Erweiterung des Iterable Interfaces in Java 8 um die Methode forEach(Consumer<? super T> action)
Iterieren über die Liste und Verarbeitung der Elemente nun getrennt
Parallelisierbar
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 4
Collection<String> myList = new LinkedList<String>();… // Liste befüllenfor ( String s : myList )System.out.println( s );
Lambda-Ausdrücke
Dieser Code sieht dafür etwas unschön aus.
Aber: Consumer<? super T> ist ein funktionales Interfaces
Daher: Verwendung eines Lambda-Ausdrucks möglich:
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 5
// Verwendung von forEachmyList.forEach(new Consumer<String>(){
@Overridepublic void accept(String arg0) {
System.out.println(arg0);}
});
// Verwendung von Lambda-Expression:myList.forEach((String element) -> System.out.println(element) );
// Oder auch:myList.forEach((element) -> System.out.println(element) );
// Oder auch:myList.forEach(element -> System.out.println(element) );
// Oder auch:myList.forEach(System.out::println);
Lambda-Ausdrücke: Syntax
Allgemeine Syntax: (Argumentenliste) -> Body
Bsp 1: (int x, int y) -> x+y
Bsp 2: (e) -> {System.out.println(e);}
Der Body kann: Ein einfacher Ausdruck sein
Wertet die Argumente aus der Argumentenliste nach dem Ausdruck aus und gibt das Ergebnis zurück
Oder ein ganzer {Block} sein
Block wird wie eine Methode ausgewertet -> Rückgabe über return Statement
Passt zu jedem Funktionsinterface, dessen einzige Methode die passende Parameterliste und den passenden Ergebnistyp verlangt.
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 6
// Beispiele: () -> 15 // Keine Parameter, Rückgabe: int
(int a) -> a + a // Parameter: 1 int, Rückgabe: int
(s) -> System.out.println(s) // Typ implizit! //Eingabe und Rückgabe: String
(e) -> System.out.println("Click") // Typ implizit!
Bekannte Beispiele
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 7
// Früher:
Runnable r = new Runnable() {
@Overridepublic void run() {
System.out.println("Hallo");}
};new Thread(r).start();
// Jetzt:
Runnable r = () -> System.out.println("Hallo");
new Thread(r).start();
// Früher:
testButton.addActionListener(new ActionListener(){@Override public void actionPerformed(ActionEvent e){
System.out.println("Click");}
});
// Jetzt:
testButton.addActionListener(e -> System.out.println("Click"));
Lambdas für Runnable
Lambdas für ActionEvents
Methoden-Referenzen
Methoden-Referenzen referenzieren Methoden ohne sie dabei aufzurufen.
Allg. Syntax: Receiver::Methodennamen
Wichtig dabei ist wieder: Parametertypen und Return-Typen müssen übereinstimmen und aus dem Kontext ersichtlich sein!
Es gibt folgende Methodenreferenzen:
Statische Methode: Klassenname::Methodenname
Instanzmethode: objektName::Methodenname
Bestimmter Typ: Typ::Methodenname
Konstruktor: Klassenname::new
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 8
// Beispiele:
Person::compareByAge // Statische Methodenreferenz
myObject::doSomething // Referenz auf eine Instanzmethode
String::compareToIgnoreCase // Referenz auf eine String-Methode
HashSet::new // Referenz auf einen Konstruktor
Noch mehr Beispiele
// Beispiel 1:ArrayList<String> myList = new
ArrayList<String>(Arrays.asList("element1","element2","element3"));
// Druchlaufen mit foreach und Lambda:myList.forEach(element -> System.out.println(element) );
// Durchlaufen mit foreach und einer Methodenreferenz:myList.forEach(System.out::println);
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 9
// Beispiel 2:
public class Programm {
public void doSomething(){
System.out.println("Hallo");System.out.println("Und noch mehr");
}public static void main(String[] args) {
Programm p = new Programm();Runnable r = p::doSomething;r.run();
}}
Default-Methoden
Wdh Folie 4: Erweiterung des Iterable Interfaces in Java 8 um die Methode forEach(Consumer<? super T> action)
Eine Implementierende Klasse muss alle Methoden eines Interfaces implementieren!
Was bedeutet das nun für älteren Code, wo es forEach noch nicht gab?
Einführung von Default-Methoden in Java 8!
Stellen eine Default-Implementierung der Methode bereit
Kann überschrieben werden, muss aber nicht!
Außer: Eine Klasse implementiert 2 Interfaces mit gleicher Default-Methode
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 10
// Beispiel: forEach im Interface Iterbale
default void forEach(Consumer<? super T> action) {Objects.requireNonNull(action);for (T t : this) {
action.accept(t);}
} // Realisiert im Prinzip das Durchlaufen einer Collection // Die accept-Methode des Interfaces Consumer muss spezifiziert werden.
Auch das Collection Framework bekommt in seinem Interface Collection<T>neue (Default)-Methoden:
default Stream<E> parallelStream()
default Stream<E> stream()
default Spliterator<E> spliterator()
Wir wollen uns im Folgenden diese Streams genauer anschauen…
Streams hatten wir bereits in der 1. Kursstunde
Sie stellen Folgen von Werten dar
Bsp.: Inputstream, Outputstream, …
Wir können aber auch Streams nutzen, um Elemente in einer Art Pipeline zu aggregieren, transformieren oder zu filtern.
Vorteil: Parallelisierbarkeit!
Default-Methoden und Collections
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 11
Stream<T> extends BaseStream in java.util definiert gängige Methoden für Streams:
anyMatch, count, empty, max, min, map, filter, findAny …
Streams selbst sind keine Datenstruktur, sondern definieren die Operationen, die auf Datensequenzen angewendet werden sollen
I.d.R. 3 Phasen:
Erzeugung
Bsp.: myCollection.stream()
Transformation
Bsp.: myCollection.stream().filter(…)
Aggregation
Bsp.: myCollection.stream().filter(…).forEach(…)
Streams
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 12
Lambdas und Streams
Dieser Code wäre wieder sehr unschön.
Hier helfen uns wieder die Lambda-Ausdrücke für eine besser Lesbarkeit und kürzerem Code:
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 13
// Beispiel:Collection<String>myList = Arrays.asList("Hello","Java");long countLongStrings = myList.stream() // Erzeugung
.filter(new Predicate<String>() { // Transformation@Overridepublic boolean test(String element) {
return element.length() > 4;}
}).count(); // Aggregation
Collection<String> myList = Arrays.asList("Hello","Java");long countLongStrings =
myList.stream() // Erzeugung.filter(element -> element.length() > 4) // Transformation.count(); // Aggregation
Stream Operationen
Streams erzeugen:
Entweder über das Collectioninterface
stream() bzw. parallelStream()
Oder mittels Generatoren:
IntStream.rangeClosed(5, 50)
Stream.of(4,6,10,34,100)
Stream.iterate(0, n -> n + 30)
Aus Dateien:
Files.lines(Paths.get("myFile.txt"), Charset.defaultCharset());
Stream.filter() stellt eine sog. intermediate operation dar:
D.h.: Die Eingabe wird bearbeitet und als modifizierter Stream zurückgegeben (Transformation) Weitere intermediate operations:
map, sorted, unsorted, distinct, limit, peek, …
Stream.count() ist eine sog. terminal operation:
D.h.: Die Operation steht am Ende der Pipeline und gibt das Ergebnis zurück (Aggregation) Weitere terminal operations (nicht alle für Streams definiert):
sum, min, max, reduce, findFirst, …27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 14
Beispiele
List<Integer> myIntList = new LinkedList<Integer>(Arrays.asList(1,3,6,8,9,23,5,2,56,54,24,7,53,253));
long listCount = myIntList.stream().count();
myIntList.stream().filter(x-> x%2 == 0).forEach(System.out::println);
myIntList.stream().filter(x-> x%2 == 0).map(x -> x * x).forEach(System.out::println);
myIntList.stream().filter(x-> x%2 == 0).map(x -> x * x).sorted().limit(4).forEach(System.out::println);
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 15
14
6 8 2 56 54 24
36 64 4 3136 2916 576
4 36 64 576
Schreiben Sie ein einfaches Programm, welches zunächst eine Liste (Bsp.: Array-List, LinkedList, o.Ä.) erzeugt und diese mit 100 Integer-Zufallszahlen füllt.
Erzeugen Sie einen Stream auf dieser Liste und implementieren Sie dann die folgenden Anfragen:
Lassen Sie sich alle Zahlen geordnet ausgeben (absteigend und aufsteigend)
Lassen Sie sich alle ungeraden Zahlen ausgeben!
Wie viele Zahlen befinden sich in Ihrer Liste, die durch 4 teilbar sind?
Welche ist die größte bzw. kleinste Zahl in Ihrer Liste?
Berechnen Sie die Quadratsumme der Zahlen und geben Sie das Ergebnis aus
Berechnen Sie den Durchschnitt der Zahlen und geben Sie das Ergebnis aus
Addieren Sie auf jede Zahl zunächst eine 5 und lassen Sie sich die Elemente ausgeben, die größer sind als 100
Aufgabe zu Streams und Lambdas
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 16
Einführung in JUnit-Tests
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 17
JUnit ist:
ein Testframework zum Testen von Java-Code (Bsp.: JUnit 4)
integraler Bestandteil der Java Developement Tools von Eclipse
geeignet für automatisiertes Testen von Modulen
Extreme Programming (test-driven software development)
Zuerst die Tests, dann der Code
Aber auch zum testen von bestehendem Code
Trennung von Test- und Programmcode:
Testklasse
Enthält Methoden zum Testen von Code
Testmethode
@Test Methode (public, void)
Testfall
Vorgehensweise (Input -> erw. Output)
Motivation & Eigenschaften
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 18
Verwendung & Syntax
Neue Testklasse erstellen:
New -> JUnit Test Case
Namen wählen
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 19
// Beispiel einer einfachen Testklasse:
import static org.junit.Assert.*;import org.junit.Test;
public class TestFeatures {
@Testpublic void testMyFeature() {
fail("Not yet implemented");}
}
Statischer importAlle statischen Methoden der Klasse Assert sind lokal verfügbar
Import von TestFür Annotations
Testmethode
Assert-Methode Test schlägt fehl.
Neben @Test existieren weitere wichtige Annotationen:
Weitere Annotationen
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 20
Annotation Erläuterung
@Test (expected = … Exception.class) Test ist nur erfolgreich, wenn die entspr. Exception geworfen wird.
@Test (timeout = … ms) Test ist nur erfolgreich, wenn die entspr. Zeit nicht überschritten wird.
@Before Wird immer vor den Tests ausgeführt. Dient für einen kontrollierten Start-Zustand
@After Wird immer nach jedem Test ausgeführt. Dient für einen sauberen Abschluss (Aufräumarbeiten)
@BeforeClass Methode muss statisch sein. Wird einmal vor dem Aufruf aller Testmethoden ausgeführt
@AfterClass Methode muss statisch sein. Wird einmal nach dem Aufruf aller Testmethoden ausgeführt
Neben fail() existieren weitere wichtige Assert-Methoden:
Weitere Assert-Methoden
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 21
Assert-Methode Erläuterung
assertEquals(Object exp, Object act) Prüft auf identische Objekte
assertFalse(boolean condition) Prüft, ob Bedingung false(Gegenstück: assertTrue)
assertNotNull(Object object) Prüft, ob Objekt instanziiert wurde. (Gegenstück: assertNull)
assertNotSame(Object unexp, Object act) Prüft, ob zwei Referenzen auf unterschiedliche Objekte verweisen. (Gegenstück: assertSame)
fail(String message) Lässt einen Test fehlschlagen und gibt die Meldung message aus.
Beispiel
public class Programm { // Programm-Code
private Collection<String> myCollection;
public Programm(){myCollection = new ArrayList<>(Arrays.asList("Peter","Uwe"));
}
public Collection<String> getMyCollection(){return this.myCollection;
}
public static void main(String[] args) {// TODO Auto-generated method stub
}}
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 22
public class TestList { //Test-Code
private Programm p;
@Beforepublic void init(){
this.p = new Programm();}
@Testpublic void testMyCollection() {
Collection<String> myList = p.getMyCollection();int result = myList.size();assertEquals(2, result);
}}
Nehmen Sie einen fertigen Code (Bspw.: blackjack von letzter Stunde) und implementieren Sie hierfür eigene Testmethoden mithilfe von JUnit 4.
Als Beispiel für den Blackjack-Code könnten Sie bspw. überprüfen,
ob ein Spieler auch eine Karte erhält, wenn er eine zieht.
ob ein Spieler zu Beginn keine Karte hat.
ob die Spieler nacheinander ziehen
ob ein Spieler wirklich aufhört, wenn er über 17 Punkte erreicht hat.
ob der Dealer die Karten auch mischt
usw.
Aufgabe zu JUnit Tests
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 23
Vielen Dank!!!
fürs Kommen
Zuhören
Mitmachen
Vielen Dank!
27.07.2017 Javakurs 11: Java 8 & JUnit - Lorenz Schauer 24