Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
InterfaceParameter und Rückgabewerte in Lambda Ausdrücken verwenden
Durch Implementierung einer anonymen Klasse konnte ich ein von mir erstelltes Interface problemlos verwenden. Ich möchte lernen, wie ich das Interface über einen Lambda-Ausdruck ansprechen kann.
Die angehängten Dateien enthalten meine Klasse Zutat und das Interface ZutatVerarbeiten.
Code einfach als Code über den </> Button im Editor. Nicht als File-Upload.
Dein auskommentierter Code in der Zutat.java: z.zubereiten( anz -> { if(anz <= ANZ_AUFLAGER) {......
funktioniert so nicht, weil die Methode zubereiten der Klasse Zutat ja weiterhin zwei Parameter erwartet. Erst der zweite Parameter ist ja das ZutatVerarbeiten Interface, für das du einen Lambda-Ausdruck verwenden kannst.
Du müsstest die Methode also so aufrufen: z.zubereiten(z.anz, anz -> { if(anz <= ANZ_AUFLAGER) {......
Insgesamt ist die objektorientierte Modellierung hier aber sehr merkwürdig.
Besten Dank für deine schnelle Hilfe! War sehr lehrreich.
Aber jetzt machst du mich noch neugierig, wie man solche Aufgaben besser angehen soll, also nicht über die objektorientierte Modellierung.
Gruß,
Cepheus
Naja, "objektorientierte Modellierung" ist halt biszchen anders als das was du machst. Naja dran, aber nicht ganz ideal.
Zuerst, kuerze keine Variablennamen ab nur weil du kannst, anz ist da ein gutes Beispiel, z ein noch viel schoeneres. Es macht den Code schwerer zu lesen und zu verstehen.
Das gesagt, du hast da ja die Zutat Klasse welche schon nahe d'ran ist, aber du mischt da noch die Verarbeitung als auch die Hauptlogik mithinein. Dein ZutatVerarbeiten ist auch kein richtiges "Zutat Verarbeiten", sondern mehr ein "Anzahl verarbeiten", und dass die Pruefung auf die Anzahl erst dort stattfindet ist vielleicht etwas spaet.
Also wenn man das etwas aufbereiten will:
Code:
Main.java
Zutat.java
ZutatVerarbeitung.java
Dann hat man in der Main.java die Hauptlogik vom Programm, Zutat tut nichts auszer Namen und Anzhal zu halten, und ZutatVerarbeitung wuerde die Zutat tatsaechlich verarbeiten. Jetzt ist hier aber das Problem dass wir die Anforderungen nicht kennen, also wir koennen dir gar nicht sagen wie man das wirklich geschickter aufbaut weil wir die Anforderungen nicht kennen. Demnach wuerde ich eher eine solche Struktur raten:
Und jetzt folgt ein biszchen Pseudo-Code wie die Umsetzung aussehen koennte.
Java:
public static final class Main() {
private Main() {
}
public static final void main(String[] arguments) {
Rezept rezept = new Rezept("Etwas");
rezept.add(new Zutat("Zwiebel", 1, Verarbeitungen.WUERFELN));
rezept.add(new Zutat("Knoblauchzehen", 3, Verarbeitungen.SCHNEIDEN));
rezept.add(new Zutat("Paprika", 2, Verarbeitungen.WUERFELN));
rezept.add(new Zutat("Bohnen", 50, Verarbeitungen.KEINE));
for (Zutat zutat : rezept.getZutaten()) {
rezept.add(zutat.verarbeiten());
}
rezept.kochen();
}
}
public interface Verarbeitung {
public VerarbeiteteZutat verarbeiten(Zutat zutat);
}
public final class Verarbeitungen {
public static final Verarbeitung KEINE = (zutat) -> new VerarbeitetZutat(zutat.getName(), zutat.getAnzahl());
public static final Verarbeitung SCHNEIDEN = (zutat) -> new VerarbeitetZutat("Geschnittene " + zutat.getName(), zutat.getAnzahl());
public static final Verarbeitung WUERFELN = (zutat) -> new VerarbeitetZutat("Gewuerfelte " + zutat.getName(), zutat.getAnzahl());
private Verarbeitungen() {
}
}
Zum Beispiel. Aber wie gesagt, ohne Anforderung etwas schwer abzuschaetzen was genau man bauen soll.
Man kann die Verarbeitungen dann auch direkt einbetten:
Also in erster Linie versucht man seine Datenhaltung und die Datenverarbeitung ein wenig von einander zu trennen. Halten von Daten ist immer einfach, da baut man sich dann ein POJO (Plain Old Java Object), also eine Klasse mit Gettern und Settern, und das verarbeiten lagert man dann irgendwo anders aus. Hier in dem Fall hat es Sinn gemacht jeder Zutat direkt den Verarbeitungsschritt mit zu geben, aber es kann auch durchaus denkbar sein dies erst (dynamisch) in der Schleife bei der Zubereitung aufzuloesen.
Also du willst quasi immer deine Verarbeitung soweit wegabstrahiert haben dass du diese relativ einfach erweitern, aendern oder austauschen kannst. Idealerweise wissen die Daten gar nichts von dem wie sie verarbeitet werden, geht sie ja nichts, und auszerdem wird sich das vermutlich ohnehin noch dreimal aendern oder erweitert werden. Von dem her koennte ich mir auch ein duales System vorstellen wo man Zutaten zusammen mit der Verarbeitung registriert im Rezept:
Java:
Rezept rezept = new Rezept("Etwas");
rezept.add(new Zutat("Zwiebeln", 5), Verarbeitung.WUERFELN);
Damit hast du dann die Zutat von der Verarbeitung komplett getrennt. Und idealerweise beziehst du die Zutaten aus einem Lagerraum:
Java:
Rezept rezept = new Rezept("Etwas");
rezept.add(lagerraum.hole("Zwiebeln", 5), Verarbeitung.WUERFELN);
Weil dann kann naemlich schon der Lagerraum einen Fehler werfen dass es nicht mehr genug Zwiebeln gibt.
Aber wie gesagt, ohne die Anforderung alles schwer zu sagen.
weil die Methode zubereiten der Klasse Zutat ja weiterhin zwei Parameter erwartet. Erst der zweite Parameter ist ja das ZutatVerarbeiten Interface, für das du einen Lambda-Ausdruck verwenden kannst.
Zusatz: OO bedeutet im Minimalfall, dass Objekte und (die sie betreffenden) Methoden über eine (oder mehrere) Klasse(n) in einen Zusammenhang gebracht werden. In Java kann man aber wie in einer Script-Sprache vorgehen wenn man nur statische Methoden / Variablen verwendet.
Ach Tobias, lies doch einfach einmal genau, was geschrieben wurde. Dann wirst selbst Du verstehen, dass die Methode des funktionalen Interfaces nur einen Parameter hat. Der Lambda Ausdruck ist also nur anz -> { if(anz <= ANZ_AUFLAGER) {......`
Die Methode, die aufgerufen wird, hat aber zwei Parameter: public boolean zubereiten(int anz, ZutatVerarbeiten z)
Somit muss man bei Aufruf erst einen Parameter für anz angeben und dann einen Parameter für ZutatVerarbeiten. Letzteres kann dann ein Lambda Ausdruck sein wie es @httpdigest gemacht hat.
Also ganz einfache Java Grundlagen - die selbst Du verstehen solltest.
Es wurde aber zum Thema, denn das ist erwähnt worden und der TE hat aktiv nachgefragt. Damit wurde es Thema. Aber ja - das setzt natürlich voraus, dass man den Threadverlauf liest und versteht. Letzteres ist natürlich nicht der Fall bei Dir, denn sonst hättest Du ja mitbekommen, dass das Lambda nur einen Parameter hat...
Guten Tag, meine Herren!
Wie man sieht, habe ich mit meiner Frage eine größere Diskussion ausgelöst. Ja, fast einen "Disput" unter den Experten. Nun kann ich aber als ganz grüner Anfänger "bezeugen", dass httpdigest in seinem Statement:
... weil die Methode zubereiten der Klasse Zutat ja weiterhin zwei Parameter erwartet. Erst der zweite Parameter ist ja das ZutatVerarbeiten Interface, für das du einen Lambda-Ausdruck verwenden kannst.
Du müsstest die Methode also so aufrufen: z.zubereiten(z.anz, anz -> { if(anz <= ANZ_AUFLAGER) {......
recht hat. Seine Empfehlung habe ich nämlich realisiert und so funktioniert es.
Ich habe von ihm etwas wichtiges gelernt: dass man sich vor dem Aufruf einer Klassenmethode deren Deklaration genau ansehen muss und darin jenen Parameter identifizieren muss (Schnittstellen-Typ!), der als ein Lambda-Ausdruck übergeben werden kann. Die anderen Parameter, also solche, deren Typ nicht das Interface sind, zählen nicht zu den Parametren des Lambda-Ausdrucks.
Ich bedanke mich bei allen Diskutanten, auch für andere nützliche Tipps (unnötige Kürzung von Variablennemen, Modellierung).
Cepheus
Naja, "objektorientierte Modellierung" ist halt biszchen anders als das was du machst. Naja dran, aber nicht ganz ideal.
Zuerst, kuerze keine Variablennamen ab nur weil du kannst, anz ist da ein gutes Beispiel, z ein noch viel schoeneres. Es macht den Code schwerer zu lesen und zu verstehen.
Das gesagt, du hast da ja die Zutat Klasse welche schon nahe d'ran ist, aber du mischt da noch die Verarbeitung als auch die Hauptlogik mithinein. Dein ZutatVerarbeiten ist auch kein richtiges "Zutat Verarbeiten", sondern mehr ein "Anzahl verarbeiten", und dass die Pruefung auf die Anzahl erst dort stattfindet ist vielleicht etwas spaet.
Also wenn man das etwas aufbereiten will:
Code:
Main.java
Zutat.java
ZutatVerarbeitung.java
Dann hat man in der Main.java die Hauptlogik vom Programm, Zutat tut nichts auszer Namen und Anzhal zu halten, und ZutatVerarbeitung wuerde die Zutat tatsaechlich verarbeiten. Jetzt ist hier aber das Problem dass wir die Anforderungen nicht kennen, also wir koennen dir gar nicht sagen wie man das wirklich geschickter aufbaut weil wir die Anforderungen nicht kennen. Demnach wuerde ich eher eine solche Struktur raten:
Und jetzt folgt ein biszchen Pseudo-Code wie die Umsetzung aussehen koennte.
Java:
public static final class Main() {
private Main() {
}
public static final void main(String[] arguments) {
Rezept rezept = new Rezept("Etwas");
rezept.add(new Zutat("Zwiebel", 1, Verarbeitungen.WUERFELN));
rezept.add(new Zutat("Knoblauchzehen", 3, Verarbeitungen.SCHNEIDEN));
rezept.add(new Zutat("Paprika", 2, Verarbeitungen.WUERFELN));
rezept.add(new Zutat("Bohnen", 50, Verarbeitungen.KEINE));
for (Zutat zutat : rezept.getZutaten()) {
rezept.add(zutat.verarbeiten());
}
rezept.kochen();
}
}
public interface Verarbeitung {
public VerarbeiteteZutat verarbeiten(Zutat zutat);
}
public final class Verarbeitungen {
public static final Verarbeitung KEINE = (zutat) -> new VerarbeitetZutat(zutat.getName(), zutat.getAnzahl());
public static final Verarbeitung SCHNEIDEN = (zutat) -> new VerarbeitetZutat("Geschnittene " + zutat.getName(), zutat.getAnzahl());
public static final Verarbeitung WUERFELN = (zutat) -> new VerarbeitetZutat("Gewuerfelte " + zutat.getName(), zutat.getAnzahl());
private Verarbeitungen() {
}
}
Zum Beispiel. Aber wie gesagt, ohne Anforderung etwas schwer abzuschaetzen was genau man bauen soll.
Man kann die Verarbeitungen dann auch direkt einbetten:
Also in erster Linie versucht man seine Datenhaltung und die Datenverarbeitung ein wenig von einander zu trennen. Halten von Daten ist immer einfach, da baut man sich dann ein POJO (Plain Old Java Object), also eine Klasse mit Gettern und Settern, und das verarbeiten lagert man dann irgendwo anders aus. Hier in dem Fall hat es Sinn gemacht jeder Zutat direkt den Verarbeitungsschritt mit zu geben, aber es kann auch durchaus denkbar sein dies erst (dynamisch) in der Schleife bei der Zubereitung aufzuloesen.
Also du willst quasi immer deine Verarbeitung soweit wegabstrahiert haben dass du diese relativ einfach erweitern, aendern oder austauschen kannst. Idealerweise wissen die Daten gar nichts von dem wie sie verarbeitet werden, geht sie ja nichts, und auszerdem wird sich das vermutlich ohnehin noch dreimal aendern oder erweitert werden. Von dem her koennte ich mir auch ein duales System vorstellen wo man Zutaten zusammen mit der Verarbeitung registriert im Rezept:
Java:
Rezept rezept = new Rezept("Etwas");
rezept.add(new Zutat("Zwiebeln", 5), Verarbeitung.WUERFELN);
Damit hast du dann die Zutat von der Verarbeitung komplett getrennt. Und idealerweise beziehst du die Zutaten aus einem Lagerraum:
Java:
Rezept rezept = new Rezept("Etwas");
rezept.add(lagerraum.hole("Zwiebeln", 5), Verarbeitung.WUERFELN);
Weil dann kann naemlich schon der Lagerraum einen Fehler werfen dass es nicht mehr genug Zwiebeln gibt.
Aber wie gesagt, ohne die Anforderung alles schwer zu sagen.
Naja, "objektorientierte Modellierung" ist halt biszchen anders als das was du machst. Naja dran, aber nicht ganz ideal.
Zuerst, kuerze keine Variablennamen ab nur weil du kannst, anz ist da ein gutes Beispiel, z ein noch viel schoeneres. Es macht den Code schwerer zu lesen und zu verstehen.
Das gesagt, du hast da ja die Zutat Klasse welche schon nahe d'ran ist, aber du mischt da noch die Verarbeitung als auch die Hauptlogik mithinein. Dein ZutatVerarbeiten ist auch kein richtiges "Zutat Verarbeiten", sondern mehr ein "Anzahl verarbeiten", und dass die Pruefung auf die Anzahl erst dort stattfindet ist vielleicht etwas spaet.
Also wenn man das etwas aufbereiten will:
Code:
Main.java
Zutat.java
ZutatVerarbeitung.java
Dann hat man in der Main.java die Hauptlogik vom Programm, Zutat tut nichts auszer Namen und Anzhal zu halten, und ZutatVerarbeitung wuerde die Zutat tatsaechlich verarbeiten. Jetzt ist hier aber das Problem dass wir die Anforderungen nicht kennen, also wir koennen dir gar nicht sagen wie man das wirklich geschickter aufbaut weil wir die Anforderungen nicht kennen. Demnach wuerde ich eher eine solche Struktur raten:
Und jetzt folgt ein biszchen Pseudo-Code wie die Umsetzung aussehen koennte.
Java:
public static final class Main() {
private Main() {
}
public static final void main(String[] arguments) {
Rezept rezept = new Rezept("Etwas");
rezept.add(new Zutat("Zwiebel", 1, Verarbeitungen.WUERFELN));
rezept.add(new Zutat("Knoblauchzehen", 3, Verarbeitungen.SCHNEIDEN));
rezept.add(new Zutat("Paprika", 2, Verarbeitungen.WUERFELN));
rezept.add(new Zutat("Bohnen", 50, Verarbeitungen.KEINE));
for (Zutat zutat : rezept.getZutaten()) {
rezept.add(zutat.verarbeiten());
}
rezept.kochen();
}
}
public interface Verarbeitung {
public VerarbeiteteZutat verarbeiten(Zutat zutat);
}
public final class Verarbeitungen {
public static final Verarbeitung KEINE = (zutat) -> new VerarbeitetZutat(zutat.getName(), zutat.getAnzahl());
public static final Verarbeitung SCHNEIDEN = (zutat) -> new VerarbeitetZutat("Geschnittene " + zutat.getName(), zutat.getAnzahl());
public static final Verarbeitung WUERFELN = (zutat) -> new VerarbeitetZutat("Gewuerfelte " + zutat.getName(), zutat.getAnzahl());
private Verarbeitungen() {
}
}
Zum Beispiel. Aber wie gesagt, ohne Anforderung etwas schwer abzuschaetzen was genau man bauen soll.
Man kann die Verarbeitungen dann auch direkt einbetten:
Also in erster Linie versucht man seine Datenhaltung und die Datenverarbeitung ein wenig von einander zu trennen. Halten von Daten ist immer einfach, da baut man sich dann ein POJO (Plain Old Java Object), also eine Klasse mit Gettern und Settern, und das verarbeiten lagert man dann irgendwo anders aus. Hier in dem Fall hat es Sinn gemacht jeder Zutat direkt den Verarbeitungsschritt mit zu geben, aber es kann auch durchaus denkbar sein dies erst (dynamisch) in der Schleife bei der Zubereitung aufzuloesen.
Also du willst quasi immer deine Verarbeitung soweit wegabstrahiert haben dass du diese relativ einfach erweitern, aendern oder austauschen kannst. Idealerweise wissen die Daten gar nichts von dem wie sie verarbeitet werden, geht sie ja nichts, und auszerdem wird sich das vermutlich ohnehin noch dreimal aendern oder erweitert werden. Von dem her koennte ich mir auch ein duales System vorstellen wo man Zutaten zusammen mit der Verarbeitung registriert im Rezept:
Java:
Rezept rezept = new Rezept("Etwas");
rezept.add(new Zutat("Zwiebeln", 5), Verarbeitung.WUERFELN);
Damit hast du dann die Zutat von der Verarbeitung komplett getrennt. Und idealerweise beziehst du die Zutaten aus einem Lagerraum:
Java:
Rezept rezept = new Rezept("Etwas");
rezept.add(lagerraum.hole("Zwiebeln", 5), Verarbeitung.WUERFELN);
Weil dann kann naemlich schon der Lagerraum einen Fehler werfen dass es nicht mehr genug Zwiebeln gibt.
Aber wie gesagt, ohne die Anforderung alles schwer zu sagen.
Dann schau einfach noch einmal ganz genau in diesen Thread. Du hast auf keine Frage geantwortet. Du hast noch nicht einmal richtig gelesen, um was es im Detail geht.
Nein, keine Diskussion. Das würde ja den Austausch von Argumenten und ein Verständnis der Sachlage erfordern. Da scheitert es aber leider bei Tobias massiv, was schade ist.
Naja - ist toll, dass Du es als running Gag ansiehst und amüsant findest. Ich finde es einfach nur peinlich. Tobias kann die Sticheleien nicht lassen - selbst in Threads, in denen er Hilfe erwartet. Jeder irrt sich mal. Jeder liest die Sachlage mal nicht gut genug. Aber wenn dann entsprechende Hinweise kommen, dann kann man da drauf eingehen. Und da stelle ich mir dann die Frage: Woran scheitert es bei Tobias? Ich habe dann extra noch die entscheidenden Java Ausschnitte gebracht in #7. Aber meine Erwartungshaltung, dass er da direkt erkennen würde, wie ein Lambda Ausdruck aussehen müsste und dass die Aufgerufene Methode zwei Parameter hat mit allen daraus notwendigen Schlußfolgerungen war wohl einfach viel zu hoch für unseren Forentroll.
Es gibt ein Buch: "Entwurfsmuster von Kopf bis Fuß" (Übersetzung von "Head First Design Patterns") von Eric Freeman / Elisabeth Robson. In diesem Buch werden sehr grafisch die wichtigsten Entwurfsmuster erläutert.
So gehen mir bei Deinem Ansatz zwei mögliche Pattern durch den Kopf, die - je nach Aufgabenstellung - Sinn machen könnten:
Strategy Pattern
Bei dem Strategy Pattern wird ein Verhalten in eigenen Klassen abgebildet. In dem Buch wird das Beispiel "Ente" genommen. Eine Ente kann Quaken. Nur eben gibt es jetzt viele Arten von Enten: Das Tier Ente, die Holzente, eine Badeente, ... Jede Quakt anders. Die Ente hat also ein Quak-Verhalten. Und da gibt es dann diverse Implementierungen, die dann der Ente zugewiesen werden kann - je nach dem, welche Art von Ente man hat. (Mal ganz grob und einfach erläutert)
Decorator Pattern
Bei dem Decoration Pattern wird ein Klasse immer erweitert. So kannst Du eine Zutat haben. Diese könntest Du dann verändern. Im Rezept hast Du sowas wie:
Die Zutat (z.B. Zwiebel) wird gewürfelt
Dann wird die Zwiebel angebraten
Dann wird mit Wein abgelöscht
....
Hier kann man das über das Dekorator Pattern alles abbilden. Dann hast Du die Zwiebel.
Die wird dann sozusagen gekapselt durch die Gewürfelte Zutat.
Das wird dann gekapselt durch das Angebratene Zutat
und so weiter.
In dem Buch wird das - so ich mich richtig erinnere - am Beispiel Pizzeria mit diversen Möglichkeiten bei Pizzen erläutert.
Diese Entwurfsmuster geben einem also ein interessantes Handwerksmaterial an die Hand, mit dem Du deine Möglichkeiten erweitern könntest.
Edit: Das Pattern heisst natürlich Decorator Pattern und nicht Decoration Pattern
Die bereits genannten Entwurfsmuster (inkl. Literatur) dienen dem Menschen ("Teile und herrsche") als Empfehlung (nicht als Gesetz) um das (häufige(re)) Ändern oder Erweitern der Software zu erleichtern. Weiters gibt es Stichworte wie "TDD", "(wie) mit Leuten reden", "Zettel und Stift","Namensgebung", "Aufteilung in Klassen", "Schichtentrennung" "people factor",,....
Den Compiler interessiert "nur" die formelle Korrektheit der Syntax (z.B. passende Anzahl an Parametern bei Funktionsaufruf, egal ob mit oder ohne Verwendung von Lambda-Ausdrücken)