Lambdas, Methodenref, fkt Programmierung ?

Niknak

Neues Mitglied
Moin zusammen!


Ich bin neu hier und beschäftige/lerne gerade Lambda-Ausdrücke und Methodenreferenzen. So ganz ist der Groschen aber noch nicht gefallen und irgendwie überschlägt sich bei jeder neuen Erklärung des Themas die ich lese alles immer weiter in meinem Kopf. 😀

Daher würde ich jetzt gerne einmal etwas "laut" vor mich hin "denken" und wäre froh, wenn mich jemand korrigiert, wenn ich falsch liege. Hoffe so wieder etwas Ordnung bei mir schaffen zu können.

Also Thema 1: Lambdas.


In erster Linie sind Lambdas ein Behälter für Quellcode, den man dann an eine Methode (?) weitergeben kann (Stichwort: Funktionale Programmierung).

Dies funktioniert in Java allerdings nur, wenn die Methode in einem funktionalen Interface deklariert wurde.

Beispielsweise habe ich folgendes Funktionales Interface:

Java:
public interface MatheInterface {
    // Funktionales Interface
    int doStuff(int a, int b);
}


Nun kann ich das in einem Lambda Ausdruck verwenden:


Java:
class Main {
    public static void main(String[] args) {
        MatheInterface add = (int a, int b) -> a + b;
        System.out.println(add.doStuff(2, 3)); //  = 5
    }
}


Ich übergebe an die Methode add die beiden Integers (linke Seite) und sage der Methode sie soll die beiden addieren. Zurück erhalte ich das Ergebnis (rechte Seite).

An dieser Stelle habe ich nur leider noch nicht verstanden, WARUM das überhaupt möglich ist. Aus Interfaces lassen sich ja für gewöhnlich keine Objekte erzeugen. Aber irgenwas muss das Interface doch implementieren?


Lambdas sind ja auch nur die Kurzschreibweise für anonyme Klassen. Also das hier oben ist nur die Kurzform für:

Java:
        MatheInterface anon = new MatheInterface() {
            @Override
            public int doStuff(int a, int b) {
                return a+b;
            }       
        };
        System.out.println(anon.doStuff(2,3));

Meine Vermutung nun: Wird im Hintergrund ein anonymes Objekt vom Typ Object erzeugt, das das Interface implementiert?


Weiteres Beispiel:
Damit man nun für solch Aufgaben nicht immer selbst ein eigenes Interface schreiben muss, gibts von Seiten Java vorgefertigte Interfaces wie "Function", "BinaryOperator" oder "Consumer".

Java:
    public static void main(String[] args) {
        List<String> lst = Arrays.asList("A", "B", "C");
        lst.forEach(x -> System.out.println((x)));
    }
    }

An die Methode forEach() kann ein Consumer fkt.Interface übergeben werden. Diesem sage ich, dass es jedes Element der Liste printen soll.
Das ist funktionale Programmierung (?).

Neben solch Spielerein sind Lambdas nützlich bei der Arbeit mit Streams und ActionHandlern (z.B. in JavaFX).

Das war soweit alles, was ich zu Lambdas weiß. Was stimmt davon und was nicht? Und hab ich noch etwas wichtiges vergessen?



Thema 2 Methodenreferenzen:

Nutzt man, wenn man in Lambdas nur eine bereits exstierende Methode aufrufen möchte. Verkürzt die Ausdrücke noch ein wenig weiter.

Beispiel mit dem Interface UnaryOperator:

Java:
        UnaryOperator<String> var = String::toUpperCase;
        System.out.println(var.apply("JavaForum"));
        // Ist die 'Kurzform' für:

        UnaryOperator<String> varLambda =  a -> a.toUpperCase();
        System.out.println(varLambda.apply("JavaForum"));


Fehlt hier noch was wichtiges?


So. Das waren meine Gedanken zu den beiden Themen. Allein das niedertippen hat sich für mich schon gelohnt ^^ So hat sich das ein oder andere schonmal etwas entwirrt. Nun würde ich mich aber freuen, wenn Ihr mich korrigieren würdet, denn alles hab ich eben noch nicht ganz verstanden und hier sicherlich auch das ein oder andere falsch ausgeführt.
 
Schau mal in die Compilierte Dateien. Wirst festellen, dass bei Anonymen Klassen eine Class Datei mit dem namen xxx$1.class oder so erstellt wird. Bei Lambdas passiert das nicht. Also sind Lambdas nicht die kurzschreibweisse von Anonymen Klassen 🙂
 
An dieser Stelle habe ich nur leider noch nicht verstanden, WARUM das überhaupt möglich ist. Aus Interfaces lassen sich ja für gewöhnlich keine Objekte erzeugen. Aber irgenwas muss das Interface doch implementieren?
Lambda Ausdrücke könnte man als anonyme Methoden bezeichnen. Die Java Laguage Specification nutzt diesen Ausdruck so zwar nicht, aber auch dort geht es in diese Richtung:
A lambda expression is like a method: it provides a list of formal parameters and a body - an expression or block - expressed in terms of those parameters.

Daher wird auch kein Interface implementiert wie bei einer Klasse. Da nur eine Methode implementiert wird, muss es ein funktionales Interface sein, ein Interface mit nur einer Methode.

Thema 2 Methodenreferenzen:

Nutzt man, wenn man in Lambdas nur eine bereits exstierende Methode aufrufen möchte.
Nein, die Methodenreferenzen werden nicht in Lambdas genutzt. Methodenreferenzen sind - wer hätte das gedacht - Referenzen auf Methoden.

Das ist also auch kein verkürzter Lambda Ausdruck oder so. Anonyme Klasse, Lambda Ausdruck und Methodenreferez sind 3 unterschiedliche Dinge, die so streng genommen nichts gemeinsam haben.

Somit läuft jetzt bei Interfaces folgende Möglichkeiten:
- Vor Java 1.8:
-- Es können Instanzen angegeben werden, die das Interface implementieren
-- Es kann eine anonyme Klasse erstellt werden, die das Interface implementiert
- Ab Java 1.8 gibt es nun noch ein funktionales Interface - dieses setzt nur die Implementation einer Methode voraus. Daher kann man lediglich eine Methode angeben:
-- Lambda Ausdruck - auch anonyme Methode nennbar
-- Methodenreferenz - also eine Referenz auf eine beliebige Methode mit passender Signatur.
Somit haben wir das jetzt wie zuvor auch schon: Bei einem funktionalen Interface wird lediglich eine Implementation der einen Methoden benötigt. Diese eine Methode kann also als anonyme Methode (Lambda Ausdruck) oder über eine Methodenreferenz angegeben werden.
 
Kurz noch dazugesagt: Lambda Ausdrücke sind faktisch Implementierungen eines Interfaces, aber eben weder durch eine "normale" Klasse noch durch eine "anonyme Klasse" (im Sinne der JLS), sondern die Klasse wird zur Laufzeit durch einen "Bootstrapping"-Prozess generiert.
Die Java Language Specification lässt hier geschickterweise natürlich die Implementierungsmethode für Lambda-Ausdrücke explizit offen, aber im Endeffekt muss es eine Instanz eines Typs sein, der zuweisungskompatibel zum Zielinterface (dem funktionalen Interface) ist, also muss es in irgendeiner Form und Weise eine Instanz einer Klasse sein.
Das ist auch in HotSpot so, und Lambda Ausdrücke (und auch Method References) werden dort als sogenannte "Hotspot anonymous classes" realisiert. Das sind seeehr anonyme Klassen, die sich nicht in die Class Hierarchy oder den ClassLoader registrieren und auch nicht per Reflection "gelookuped" werden können.
 
Erstmal vielen Dank für Eure Antworten!

Okay Lambda != Anonyme Klasse. Und warum die so funktionieren wie sie funktionieren, ist wohl nicht so trivial. Als Anfänger begnüge ich mich glaube ich vorerst mit der Erklärung von @KonradN: Lambda ist eine anonyme Methode. Darunter kann ich mir jetzt noch am ehesten etwas vorstellen. ^^

Bezgl. der Methodenreferenzen: Das werde ich mir nochmal etwas genauer ansehen müssen.

Vllt wähne ich mich jetzt auch in falscher Sicherheit, aber wenn der Rest den ich geschrieben habe nicht ganz falsch war, bin ich jetzt schonmal happy. 😀
 
Nichts macht jemals einen Sinn, aber manchmal ergibt es welchen.
Sinnvoll ist, das zu verwenden, das den Code am einfachsten wartbar und lesbar macht.
Hat der Aufruf einen Spaghetticode von 200 Zeilen, ist das zwar schon per se unsinnig, aber in dem Fall würde ich auf jeden Fall auch bei einmaliger Verwendung in eine Methode auslagern.
Hat man eine Methode, die genau einen Zweck hat und deren Name eindeutig beschreibt, was sie macht, dann sollte das auf jeden Fall so bleiben, egal wie oft sie aufgerufen wird.
 
Bezgl. der Methodenreferenzen: Das werde ich mir nochmal etwas genauer ansehen müssen.
Kleines Beispiel:
Java:
List.of("A","B","C").forEach(new Consumer<String>() {
        public void accept(String elem) {
            System.out.println(elem);
        }
    });
Die Methode forEach erwartet ganz allgemein einen Consumer<? super T>, wobei T hier der Typ der List, also String, ist.

Consumer ist ein funktionales Interface, es gibt also nur eine (deklarierte aber nicht definierte) Methode, nämlich eine, die einen String (oder eine Superklasse von String, sprich: Object) erwartet und nichts zurückgibt. Im Prinzip könnte man also jede x-beliebige Methode als Consumer verwenden, die ebenso einen String (oder ein Object) erwartet und nichts zurückgibt.

Dabei heißt "jede x-beliebige", dass auch Methoden ohne Namen möglich wären, die man mit Hilfe eines Lambda-Ausdrucks angeben kann:
Java:
List.of("A","B","C").forEach(elem -> System.out.println(elem));

Wenn wir uns System.out.println() genauer ansehen, dann gibt es dort eine Methode, die selbst schon den Anforderungen genügt: eine Methode, die einen String erwartet und nichts zurückgibt.

Jetzt wäre es doch schön, wenn man statt des Lambda-Ausdrucks auch direkt die Methode System.out.println verwenden könnte, man müsste nur irgendwie eine Referenz zu dieser Methode angeben können... Huch:
Java:
List.of("A","B","C").forEach(System.out::println);
🙂
 

Zurück
Oben