Ziele dieses Praktikums sind:
-
Sie können Lambda Expressions schreiben
-
Sie können eigene funktionale Interfaces schreiben und verwenden
-
Sie können
Optionalsinnvoll anwenden -
Sie kennen Methoden-Referenzen und können diese einsetzen
-
Sie kennen die wichtigsten Klassen und Methoden aus
java.util.streamundjava.util.functionund können diese anwenden
Das Praktikum enthält verschiedene Arten von Aufgaben, die wie folgt gekennzeichnet sind:
- [TU] – Theoretische Übung
-
Dient der Repetition bzw. Vertiefung des Stoffes aus der Vorlesung und als Vorbereitung für die nachfolgenden Übungen.
- [PU] – Praktische Übung
-
Übungsaufgaben zur praktischen Vertiefung von Teilaspekten des behandelten Themas.
- [PA] – Pflichtaufgabe
-
Übergreifende Aufgabe zum Abschluss. Das Lösen dieser Aufgaben ist Pflicht. Sie muss bis zum definierten Zeitpunkt abgegeben werden, wird bewertet und ist Teil der Vornote.
Für das Praktikum stehen die Wochen gemäss den Angaben in Moodle zur Verfügung.
Je nach Kenntnis- und Erfahrungsstufe benötigen Sie mehr oder weniger Zeit.
Nutzen Sie die Gelegenheit den Stoff zu vertiefen, auszuprobieren, Fragen zu stellen und Lösungen zu diskutieren.
Falls Sie das Thema schon beherrschen, müssen Sie nur die Pflichtaufgaben lösen und bis zum angegebenen Zeitpunkt abgeben (Fast-Track).
Die Pflichtaufgaben werden mit 0 bis 2 Punkten bewertet (siehe Leistungsnachweise auf Moodle).
|
💡
|
Auch wenn Sie das Thema schon beherrschen, prüfen Sie bitte Ihr Wissen über das Design Pattern Chain of responsibility. |
|
ℹ️
|
Sie können Textantworten in der Datei solutions-sheet.adoc (eine Muster-Datei ist im Code-Verzeichnis) oder
solutions-sheet.md im Root-Verzeichnis der Übung sammeln.
|
Java bietet für viele Zwecke im Package java.util.functional Functional Interfaces.
-
Welche Interfaces aus dem Package
java.util.functionkönnen Sie alles nutzen, um-
die mathematische Funktion f(x) = x ^ 2 - 3 für Zahlen des Typs
longabzubilden? -
um den Zinsfaktor (double) für n (int) Jahre bei einem Zinssatz von p Prozent (float) zu berechnen mit der Formel zf = (1 + p / 100)^n ?
-
ein Objekt vom Typ
Person(ohne Parameter) zu generieren?
-
-
Welche Eigenschaft muss eine Funktion haben, damit Sie ein eigenes Interface schreiben müssen, also keines der in
java.util.functionvorhandenen Interfaces verwenden können? -
Welche der Aussagen stimmen für ein funktionales Interface?
-
❏ Es ist ein Java-Interface (Schlüsselwort
interfaceim Code) -
❏ Es hat genau eine abstrakte Methode
-
❏ Das Interface muss mit
@FunctionalInterfacemarkiert sein -
❏ Es hat keine default-Methoden (Schlüsselwort
default)
-
-
Welche Aussagen stimmen?
-
❏ Zu jedem funktionalen Interface können Lambda-Ausdrücke (lambda expressions) geschrieben werden
-
❏ Ein Lambda-Ausdruck kann ohne passendes funktionales Interface erstellt werden
-
❏ Eine Variable vom Typ
Optionalkann nienullsein.
-
Starten Sie den Kurs Java. Functional programming. Dazu müssen Sie dort ein Konto anlegen. Die Plattform ist von der ZHAW unabhängig.
|
💡
|
Sie können dort alle Aufgaben direkt im Browser lösen. Oft ist es aber zweckmässig, den Code in die IDE zu übernehmen und die Lösung dort zu entwickeln. |
Auf dieser Plattform wird Ihre Lösung online geprüft und Sie erhalten Feedback, ob Ihre Lösung alle Tests erfüllt.
|
💡
|
Wenn Sie eine funktionierende Lösung abgegeben haben, erhalten Sie Zugriff auf Kommentare und Lösungen anderer Personen. Vergleichen Sie Ihre Lösung, Sie können viel von anderen Lösungen lernen. |
Lösen Sie die folgenden Übungen:
-
Lösen Sie 2.5 Calculating production of all numbers in the range
Tipp: Verwenden Sie die passend Methode
.reduce(…) -
Lösen Sie 2.6 Getting distinct strings
-
Lösen Sie die Übung 3.7 Composing predicates. Die Aufgabe verlangt, dass Sie ein
IntPredicateerstellen, das alleIntPredicateaus übergebenen Listepredicatesmit der Oder-Funktion (or) verknüpft. Eine mögliche Lösung istclass Predicate { public static IntPredicate disjunctAll(List<IntPredicate> predicates) { IntPredicate disjunct = x -> false; for(IntPredicate currentPredicate: predicates) { disjunct = disjunct.or(currentPredicate); } return disjunct; } }
Eine Anwendung könnte sein:
class Predicate { public static void main(String[] args) { IntPredicate isEven = x -> x % 2 == 0; IntPredicate isDividableBy3 = x -> x % 3 == 0; List<IntPredicate> predicateList = List.of(isEven, isDividableBy3); IntPredicate disPredicate = disjunctAll(predicateList); IntStream.range(1, 10).forEach(i -> System.out.printf("%2d -> %s%n", i, disPredicate.test(i))); } }
Suchen Sie jedoch eine Lösung, die mit Streams arbeitet. Sie finden Tests und ein Gerüst für die Aufgabe in
code/Stepikin der KlasseComposingPredicate.💡Wenn Sie eine Lösung gefunden haben, überlegen Sie sich, wie viele Funktionen ( IntPredicate) beim Aufruf von.test()ausgewertet werden. Lässt sich dies reduzieren? -
Lösen Sie die folgenden Aufgaben mit Streams:
-
4.6 Numbers filtering - beachten Sie die Methode IntStream.concat
-
5.3 Collectors in practice: the product of squares
In den Folien der Vorlesung sind die
Stream.reduce()-Methoden aufgeführt. In der Aufgabe wird aberStream.collect(collector)verwendet und Sie müssen nur den collector angeben. Die entsprechenden Funktionen in der Collectors-Klasse heissenCollectors.reducing(). Ihre Lösung lautet alsoCollectors.reducing(…) -
5.5 Almost like a SQL: the total sum of transactions by each account
Tipp: Auch wenn steht, dass die Form
Collectors.reducingverwendet werden kann, ist die Methodereducingnicht die Lösung, sie benötigen eine andere Methode aus der KlasseCollectors.
-
Lernen Sie das Pattern Chain of Responsibility kennen.
In der Übung 3.9 The chain of responsibility pattern in the functional style setzen Sie dieses Pattern funktional um.
|
💡
|
Das ist eine aufwändige Aufgabe, nehmen Sie sich Zeit dafür. |
|
🔥
|
Bei dieser Aufgabe geht es darum alles mit Streams zu lösen. Verwenden Sie keine for-, do-, oder while-Schleifen. |
Im Package ch.zhaw.prog2.functional.streaming finden Sie einige Klassen. Diese ermöglichen einer Firma den Angestellten die Löhne auszubezahlen.
Zu den Klassen sind auch passende Tests für die Klassen vorhanden.
Für die Tests werden die Objekte mit generierten Daten angereichert.
|
💡
|
Sie sollen nur die folgenden Klassen anpassen:
|
Lösen Sie mit Hilfe von Streams und basierend auf diesem existierenden Code die folgenden Aufgaben:
-
Mit
Company.allEmployees()erhalten Sie alle Angestellten.Implementieren Sie die Methoden
Company.getDistinctFirstnamesOfEmployees()undCompany.getDistinctLastnamesOfEmployees().Die dazugehörigen Tests sind in
CompanyTestbereits vorhanden.💡Die Implementation benötigt keine Hilfsvariablen. Sie können die Implementation mit return getAllEmployees().stream()starten. -
Mit
Employee.isWorkingForCompanykönnen Sie prüfen, ob der Angestellte noch für die Firma tätig ist. Implementieren SieCompany.getEmployeesWorkingForCompany().
Der dazugehörige Test ist inCompanyTestbereits vorhanden. -
Als Nächstes sollen alle Angestellten mit dem Attribut
Employee.isFemaleermittelt werden. Da dies zu ähnlichem Code wie in der vorherigen Aufgabe führt, realisieren Sie eine generischere MethodeCompany.getEmployeesByPredicate(Predicate<Employee>). Die dazugehörigen Tests schreiben Sie in der TestklasseCompanyTestStudent. Als Tests schlage ich vor zu prüfen, ob die Summe der Angestellten mit dem AttributisFemaleund ohne dieses Attribut gleich der Summe aller Angestellten ist. -
Nachdem
Companyuns Methoden für den Zugriff auf die Angestellten bietet, kümmern wir uns um die Lohnzahlungen. Die KlassePayrollsammeltPaymentin einer Liste. In der KlassePayrollCreatorschreiben Sie die dazu nötigen Methoden.Implementieren Sie die Methode
PayrollCreator.getPayrollForAll(), die einePayrollfür alle Angestellten erstellt, für dieEmployee.isWorkingForCompanygesetzt ist. Verwenden Sie die MethodeCompany.getPayments.Einen passenden Test finden Sie in
PayrollCreatorTest. -
Wie hoch ist nun die Lohnsumme? Implementieren Sie
PayrollCreator.payrollValueCHF().Da verschiedene Währungen verwendet werden, müssen die
Paymentmit der MethodeCurrencyChange.getInNewCurrencyzu CHF gewandelt werden. -
Nun sollen noch die Summen pro Währung ermittelt werden. Implementieren Sie die Methode
PayrollCreator.payrollAmountByCurrency.Ein Ansatz dazu kann Ihnen das Tutorial über Reduction mit Streams geben.
Schreiben Sie einen Test dazu in
PayrollCreatorTestStudent. Verwenden Sie Mocking. Ein Positiv-Test, der prüft, dass die Währungen bei der Summenbildung korrekt berücksichtigt werden, reicht für diese Aufgabe aus. -
In der Methode
Company.getPayments(Predicate)ist bisher nicht berücksichtigt, dass der 13. Monatslohn nicht gleichmässig über das Jahr ausbezahlt wird.ℹ️Bei einer Anstellung mit einem 13. Monatslohn wird zu den 12 monatlichen Lohnzahlungen ein weiteres Monatsgehalt ausbezahlt. Das monatliche Gehalt ist dann 1/13 des Jahresgehalts. In der Regel wird der 13. Monatslohn im Dezember ausbezahlt. Der 13. Monatslohn soll nur im Dezember ausbezahlt werden. Zudem gibt es gelegentlich andere Anpassungen, z.B. 5% firmenweite Sondervergütung. Um dies flexibel definieren zu können, soll die anzuwendende Lohnberechnung in einer Funktion übergeben werden.
Orientieren Sie sich an der Funktion
Company.getPayments(Predicate)und implementieren SieCompany.getPayments(Predicate, Function).Implementieren Sie auch die dazu passenden Funktionen
Company.paymentForEmployeeDecemberfür Zahlungen mit dem 13. Monatslohn undCompany.paymentForEmployeeJanuaryfür Zahlungen ohne 13. Monatslohn. Die dazu nötigen Deklarationen finden Sie inCompanyam Anfang der Klasse.
