{"id":1630,"date":"2012-12-24T10:56:32","date_gmt":"2012-12-24T08:56:32","guid":{"rendered":"http:\/\/www.tutego.de\/blog\/javainsel\/?p=1630"},"modified":"2017-08-10T16:22:18","modified_gmt":"2017-08-10T14:22:18","slug":"einfhrung-in-java-8-lambda-ausdrcke-code-sind-daten","status":"publish","type":"post","link":"https:\/\/www.tutego.de\/blog\/javainsel\/2012\/12\/einfhrung-in-java-8-lambda-ausdrcke-code-sind-daten\/","title":{"rendered":"Einf&uuml;hrung in Java 8 Lambda-Ausdr&uuml;cke: Code sind Daten"},"content":{"rendered":"<p>Wer den Begriff \u201eDaten\u201c h\u00f6rt, denkt zun\u00e4chst einmal an Zahlen, Bytes, Zeichenketten oder auch komplexe Objekte mit ihrem Zustand. Wir wollen in diesem Kapitel diese Sicht ein wenig erweitern und auf Programmcode lenken. Java-Code, versinnbildlicht als Serie von Bytecodes, besteht auch aus Daten. Und wenn wir uns einmal auf diese Sichtweise einlassen, dass Code gleich Daten ist, dann l\u00e4sst sich Code auch wie Daten \u00fcbergeben und so von einem Punkt zum anderen \u00fcbertragen, speichern und sp\u00e4ter referenzieren. Mit dieser M\u00f6glichkeit, Code zu \u00fcbertragen, l\u00e4sst sich das Verhalten von Algorithmen leicht anpassen. Beginnen wir mit ein paar Beispielen, bei denen Programmcode \u00fcbergeben wird, auf den dann sp\u00e4ter zugegriffen wird:<\/p>\n<ul>\n<li>Ein Thread f\u00fchrt Programmcode im Hintergrund aus. Der Programmcode, den der Java-Thread ausf\u00fchren soll, wird in ein Objekt vom Typ Runnable verpackt, genau genommen in eine run()-Methode gesetzt. Kommt der Thread zum Zuge, ruft er die run()-Methode auf.<\/li>\n<li>Ein Timer ist eine util-Klasse, die zu bestimmen Zeitpunkten Programmcode ausf\u00fchren kann. Der Objektmethode scheduleAtFixedRate(\u2026) wird dabei ein Objekt vom Typ TimerTask \u00fcbergeben, das den Programmcode enth\u00e4lt.<\/li>\n<li>Zum Sortieren von Daten kann eine eigene Ordnung definiert werden, die dem Sortierer als Comparator \u00fcbergeben werden kann. Der Comparator deklariert eine Vergleichsmethode, an die sich der Sortierer wendet, um zwei Objekte in die gew\u00fcnschte Reihenfolge zu bringen.<\/li>\n<li>Aktiviert der Benutzer auf der Oberfl\u00e4che eine Schaltfl\u00e4che, so f\u00fchrt das zu einer Aktion. Der Programmcode steckt \u2013 beim UI-Framework Swing \u2013 in einem Objekt vom Typ ActionListener und wird an der Schaltfl\u00e4che JButton mit addActionListener(\u2026) fest gemacht. Kommt es zu einer Schaltfl\u00e4chenaktivierung, arbeitet das UI-System den Programmcode in der Methode actionPerformed(\u2026) des gespeicherten ActionListener<\/li>\n<\/ul>\n<p>Um Programmcode von einer Stelle zur anderen zu bringen, wird in Java immer der gleiche Mechanismus eingesetzt: Eine Klasse implementiert eine (in der Regel nichtstatische) Methode, in der der auszuf\u00fchrende Programmcode steht. Ein Objekt dieser Klasse wird an eine andere Stelle \u00fcbergeben, und der Interessent greift dann \u00fcber die Methode auf den Programmcode zu. Dass ein Objekt noch mehr als diese eine Implementierung enthalten kann, etwa Variablen, Konstanten, Konstruktoren, ist daf\u00fcr nicht relevant. Diesen Mechanismus schauen wir uns jetzt in verschiedenen Varianten genauer an.<\/p>\n<h4>Innere Klassen als Code-Transporter<\/h4>\n<p>Bleiben wir bei dem Beispiel mit den Vergleichen. Angenommen, wir sollen Strings so sortieren, dass Leerraum vorne und hinten bei den Vergleichen ignoriert wird, also &#8220; Newton &#8220; gleich &#8222;Newton&#8220; ist. Bei Vorgaben dieser Art muss einem Sortieralgorithmus ein St\u00fcckchen Code \u00fcbergeben werden, damit er die korrekte Reihenfolge herstellen kann. Praktisch sieht das so aus:<\/p>\n<pre>import\u00a0java.util.*;\r\n public\u00a0class\u00a0CompareTrimmedStrings\u00a0{\r\n \u00a0\u00a0public\u00a0static\u00a0void\u00a0main(\u00a0String[]\u00a0args\u00a0)\u00a0{\r\n \u00a0\u00a0\u00a0\u00a0class\u00a0TrimmingComparator\u00a0implements\u00a0Comparator&lt;String&gt;\u00a0{\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0@Override\u00a0public\u00a0int\u00a0compare(\u00a0String\u00a0s1,\u00a0String\u00a0s2\u00a0)\u00a0{\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return\u00a0s1.trim().compareTo(\u00a0s2.trim()\u00a0);\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\r\n \u00a0\u00a0\u00a0\u00a0}\r\n \u00a0\u00a0\u00a0\u00a0String[]\u00a0words\u00a0=\u00a0{\u00a0\"M\",\u00a0\"\\nSkyfall\",\u00a0\"\u00a0Q\",\u00a0\"\\t\\tAdele\\t\"\u00a0};\r\n \u00a0\u00a0\u00a0\u00a0Arrays.sort(\u00a0words,\u00a0new\u00a0TrimmingComparator()\u00a0);\r\n \u00a0\u00a0\u00a0\u00a0System.out.println(\u00a0Arrays.toString(\u00a0words\u00a0)\u00a0);\r\n \u00a0\u00a0}\r\n }<\/pre>\n<p>Die Ausgabe ist:<\/p>\n<pre>[\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Adele\u00a0\u00a0\u00a0\u00a0,\u00a0M,\u00a0\u00a0Q,\u00a0\r\n Skyfall]<\/pre>\n<p>Der TrimmingComparator enth\u00e4lt in der compare(\u2026)-Methode den Programmcode f\u00fcr die Vergleichslogik. Ein Exemplar vom TrimmingComparator wird aufgebaut und Arrays.sort(\u2026) \u00fcbergeben. Das geht mit weniger Code!<\/p>\n<h4>Innere anonyme Klassen als Code-Transporter<\/h4>\n<p>Klassen enthalten Programmcode, und Exemplare der Klassen werden an Methoden wie sort(\u2026) \u00fcbergeben, damit der Programmcode dort hinkommt, wo er gebraucht wird. Doch elegant ist das nicht. F\u00fcr die Beschreibung des Programmcodes ist extra eine eigene Klasse erforderlich. Das ist viel Schreibarbeit, und \u00fcber eine innere anonyme Klasse l\u00e4sst sich der Programmcode schon ein wenig verk\u00fcrzen:<\/p>\n<pre>String[]\u00a0words\u00a0=\u00a0{\u00a0\"M\",\u00a0\"\\nSkyfall\",\u00a0\"\u00a0Q\",\u00a0\"\\t\\tAdele\\t\"\u00a0};\r\n Arrays.sort(\u00a0words,\u00a0new\u00a0Comparator&lt;String&gt;()\u00a0{\r\n \u00a0\u00a0@Override\u00a0public\u00a0int\u00a0compare(\u00a0String\u00a0s1,\u00a0String\u00a0s2\u00a0)\u00a0{\r\n \u00a0\u00a0\u00a0\u00a0return\u00a0s1.trim().compareTo(\u00a0s2.trim()\u00a0);\r\n \u00a0\u00a0}\u00a0}\u00a0);\r\n System.out.println(\u00a0Arrays.toString(\u00a0words\u00a0)\u00a0);<\/pre>\n<p>Allerdings ist das immer noch aufw\u00e4ndig: Wir m\u00fcssen eine Methode \u00fcberschreiben und dann ein Objekt aufbauen. F\u00fcr Programmautoren ist das l\u00e4stig, und die JVM hat es mit vielen \u00fcberfl\u00fcssigen Klassendeklarationen zu tun. Die Frage ist: Wenn der Compiler wei\u00df, dass bei sort(\u2026) ein Comparator n\u00f6tig ist, und wenn ein Comparator sowieso nur eine Methode hat, muss dann Comparator und compare(\u2026) \u00fcberhaupt genannt werden?<\/p>\n<h4>Abk\u00fcrzende Schreibweise durch Lambda-Ausdr\u00fccke<\/h4>\n<p>Mit Lambda-Ausdr\u00fccken l\u00e4sst sich Programmcode leichter an eine Methode \u00fcbergeben, denn es gibt eine kompakte Syntax f\u00fcr die Implementierung von Schnittstellen mit einer Operation. F\u00fcr unser Beispiel sieht das so aus:<\/p>\n<pre>String[]\u00a0words\u00a0=\u00a0{\u00a0\"M\",\u00a0\"\\nSkyfall\",\u00a0\"\u00a0Q\",\u00a0\"\\t\\tAdele\\t\"\u00a0};\r\n Arrays.sort(\u00a0words,\r\n \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0(String\u00a0s1,\u00a0String\u00a0s2)\u00a0-&gt;\u00a0{\u00a0return\u00a0s1.trim().compareTo(s2.trim());\u00a0}\u00a0);\r\n System.out.println(\u00a0Arrays.toString(\u00a0words\u00a0)\u00a0);<\/pre>\n<p>Der in fett gesetzte Ausdruck nennt sich Lambda-Ausdruck. Er ist eine kompakte Art und Weise, Schnittstellen mit genau einer Methode zu implementieren; die Schnittstelle Comparator hat genau eine Operation compare(\u2026).<\/p>\n<p>Optisch sind sich ein Lambda-Ausdruck und eine Methodendeklaration \u00e4hnlich; was wegf\u00e4llt sind Modifizierer, der R\u00fcckgabetyp, der Methodenname und (m\u00f6gliche) throws-Klauseln.<\/p>\n<table>\n<tbody>\n<tr>\n<td width=\"318\">Methodendeklaration<\/td>\n<td width=\"318\">Lambda-Ausdruck<\/td>\n<\/tr>\n<tr>\n<td width=\"318\">public\u00a0int\u00a0compare<br \/>\n(\u00a0String\u00a0s1,\u00a0String\u00a0s2\u00a0)<\/p>\n<p>{\u00a0return\u00a0s1.trim().compareTo(\u00a0s2.trim()\u00a0);\u00a0}<\/td>\n<td width=\"318\">\n(\u00a0String\u00a0s1,\u00a0String\u00a0s2\u00a0)<br \/>\n-&gt;<br \/>\n{\u00a0return\u00a0s1.trim().compareTo(\u00a0s2.trim()\u00a0);\u00a0}<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Tabelle 1.1: Vergleich der Methodendeklaration einer Schnittstelle mit dem Lambda-Ausdruck<\/p>\n<p>Wenn wir uns den Lambda-Ausdruck als Implementierung dieser Schnittstelle anschauen, dann l\u00e4sst sich dort nichts von Comparator oder compare(\u2026) ablesen \u2013 ein Lambda-Ausdruck repr\u00e4sentiert mehr oder weniger nur den Java-Code und l\u00e4sst das, was der Compiler aus dem Kontext herleiten kann, weg.<\/p>\n<p>Alle Lambda-Ausdr\u00fccke lassen sich in einer Syntax formulieren, die die folgende allgemeine Form hat:<\/p>\n<pre>(\u00a0LambdaParameter\u00a0)\u00a0-&gt;\u00a0{\u00a0Anweisungen\u00a0}<\/pre>\n<p>Lambda-Parameter sind sozusagen die Eingabewerte f\u00fcr die Anweisungen. Die Parameterliste wird so deklariert, wie von Methoden oder Konstruktoren bekannt, allerdings gibt es keine Varargs. Es gibt syntaktische Abk\u00fcrzungen, wie wir sp\u00e4ter sehen werden, doch vorerst bleiben wir bei dieser Schreibweise.<\/p>\n<p><strong>Geschichte:\u00a0<\/strong>Der Java-Begriff \u201eLambda-Ausdruck\u201c geht auf das Lambda-Kalk\u00fcl (in der englischen Literatur Lambda calculus genannt, auch geschrieben als \u03bb-calculus) aus den 1930er Jahren zur\u00fcck und ist eine formale Sprache zur Untersuchung von Funktionen.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wer den Begriff \u201eDaten\u201c h\u00f6rt, denkt zun\u00e4chst einmal an Zahlen, Bytes, Zeichenketten oder auch komplexe Objekte mit ihrem Zustand. Wir wollen in diesem Kapitel diese Sicht ein wenig erweitern und auf Programmcode lenken. Java-Code, versinnbildlicht als Serie von Bytecodes, besteht auch aus Daten. Und wenn wir uns einmal auf diese Sichtweise einlassen, dass Code gleich [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","_links_to":"","_links_to_target":""},"categories":[11,66],"tags":[],"class_list":["post-1630","post","type-post","status-publish","format-standard","hentry","category-insel","category-java-8"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/1630","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/comments?post=1630"}],"version-history":[{"count":2,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/1630\/revisions"}],"predecessor-version":[{"id":3954,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/1630\/revisions\/3954"}],"wp:attachment":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/media?parent=1630"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/categories?post=1630"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/tags?post=1630"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}