{"id":2731,"date":"2014-03-21T11:46:33","date_gmt":"2014-03-21T09:46:33","guid":{"rendered":"http:\/\/www.tutego.de\/blog\/javainsel\/?p=2731"},"modified":"2017-06-19T14:11:24","modified_gmt":"2017-06-19T12:11:24","slug":"java-8-syntax-lambda-ausdruecke","status":"publish","type":"post","link":"https:\/\/www.tutego.de\/blog\/javainsel\/2014\/03\/java-8-syntax-lambda-ausdruecke\/","title":{"rendered":"Syntax f\u00fcr Lambda-Ausdr\u00fccke"},"content":{"rendered":"<p>Lambda-Ausdr\u00fccke haben wie Methoden m\u00f6gliche Parameter- und R\u00fcckgabe-Werte. Die Java-Grammatik f\u00fcr die Schreibweise von Lambda-Ausdr\u00fccken sieht ein paar n\u00fctzliche syntaktische Abk\u00fcrzungen vor.<\/p>\n<h4>Ausf\u00fchrliche Schreibweise<\/h4>\n<p>Lambda-Ausdr\u00fccke lassen sich auf unterschiedliche Arten und Weisen schreiben, da es f\u00fcr diverse Konstruktionen Abk\u00fcrzungen gibt. Eine Form, die jedoch immer gilt ist:<\/p>\n<p>&#8218;(&#8218; <i>LambdaParameter<\/i> &#8218;)&#8216; &#8218;-&gt;&#8216; &#8218;{&#8218; <i>Anweisungen<\/i> &#8218;}&#8216;<\/p>\n<p>Der Lambda-Parameter besteht (voll ausgeschrieben) wie ein Methodenparameter aus a) dem Typ, b) dem Namen und c) optionalen Modifizieren.<\/p>\n<p>Der Parametername \u00f6ffnet einen neuen G\u00fcltigkeitsbereich f\u00fcr eine Variable, wobei der Parametername <i>keine<\/i> anderen Namen von lokalen Variablen \u00fcberlagern darf. Hier verh\u00e4lt sich die Lambda-Parametervariable wie eine neue Variable aus einem inneren Block und nicht wie eine <span style=\"text-decoration: underline;\">Variable<\/span> aus einer inneren Klasse, wo die Sichtbarkeit anders ist.<\/p>\n<p>Beispiel<\/p>\n<p>Folgendes gibt einen Compilerfehler im Lambda-Ausdruck, weil var schon deklariert ist, die Parametervariable vom Lambda-Ausdruck muss also \u201efrisch\u201c sein:<\/p>\n<pre>String <b>var<\/b> = \"\";\r\nvar.chars().forEach( <b>var<\/b> -&gt; { System.out.println( var ); } );\u00a0 \/\/ \u00a0Compilerfehler<\/pre>\n<h4>Abk\u00fcrzung 1: Typinferenz<\/h4>\n<p>Der Java-Compiler kann viele Typen aus dem Kontext ablesen, was Typ-Inferenz genannt wird. Wir kennen so etwas vom Diamant-Operator, wenn wir etwa schreiben List&lt;String&gt; list = new ArrayList&lt;&gt;().<\/p>\n<p>Sind f\u00fcr den Compiler genug Typ-Informationen verf\u00fcgbar, dann erlaubt der Compiler bei Lambda-Ausdr\u00fccken eine Abk\u00fcrzung. Bei<\/p>\n<pre>Comparator&lt;String&gt; c =\r\n\u00a0 (<b>String s1, String s2<\/b>) -&gt; { return s1.trim().compareTo( s2.trim() ); };<\/pre>\n<p>ist Typ-Inferenz einfach (Comparator&lt;String&gt; sagt alles aus), daher funktioniert die folgende\u00a0 Abk\u00fcrzung:<\/p>\n<pre>Comparator&lt;String&gt; c = (<b>s1, s2<\/b>) -&gt; { return s1.trim().compareTo( s2.trim() ); };<\/pre>\n<p>Die Parameterliste enth\u00e4lt also entweder explizit deklarierte Parametertypen oder implizite inferred-Typen. Eine Mischung ist nicht erlaubt, der Compiler blockt so etwas wie (String s1, s2) oder (s1, String s2) mit einem Fehler ab.<\/p>\n<p>Wenn der Compiler die Typen ablesen kann, sind die Parametertypen optional. Aber Typ-Inferenz ist nicht immer m\u00f6glich, weshalb die Abk\u00fcrzung nicht immer m\u00f6glich ist. Au\u00dferdem hilft die explizite Schreibweise auch der Lesbarkeit: kurze Ausdr\u00fccke sind nicht unbedingt die verst\u00e4ndlichsten.<\/p>\n<p>Hinweis<\/p>\n<p>Der Compiler liest aus den Typen ab, ob alle Eigenschaften vorhanden sind. Die Typen sind dabei entweder explizit oder implizit gegeben.<\/p>\n<pre>Comparator&lt;<b>String<\/b>&gt; sc = (a, b) -&gt; { return Integer.compare( a.length(), b.length() ); };\r\nComparator&lt;<b>BitSet<\/b>&gt; bc = (a, b) -&gt; { return Integer.compare( a.length(), b.length() ); };<\/pre>\n<p>Die Klassen String und BitSet besitzen beide die Methode length(), daher ist der Lambda-Ausdruck korrekt. Der gleiche Lambda-Code l\u00e4sst sich f\u00fcr zwei v\u00f6llig verschiedene Klassen einsetzen, die \u00fcberhaupt keine Gemeinsamkeiten haben, nur das sie zuf\u00e4llig beide eine Methode namens length() besitzen.<\/p>\n<h4>Abk\u00fcrzung 2: Lambda-Rumpf ist entweder einzelner Ausdruck oder Block<\/h4>\n<p>Besteht der Rumpf eines Lambda-Ausdrucks nur aus einem einzelnen Ausdruck, kann eine verk\u00fcrzte Schreibweise die Block-Klammern und das Semikolon einsparen. Statt<\/p>\n<p>( <i>LambdaParameter<\/i> ) -&gt; { return <i>Ausdruck;<\/i> }<\/p>\n<p>hei\u00dft es dann<\/p>\n<p>( <i>LambdaParameter<\/i> ) -&gt; <i>Ausdruck<\/i><\/p>\n<p>Lambda-Ausdr\u00fccke mit einer return\u2013Anweisung im Rumpf kommen h\u00e4ufig vor, da dies den typischen Funktionen entspricht. Somit ist es eine willkommene Verk\u00fcrzung, wenn die abgek\u00fcrzte Syntax f\u00fcr Lambda-Ausdr\u00fccke lediglich den Ausdruck fordert, der dann die R\u00fcckgabe bildet.<\/p>\n<p>Hier sind drei Beispiele:<\/p>\n<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"306\">Lange Schreibweise<\/td>\n<td valign=\"top\" width=\"306\">Abk\u00fcrzung<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"306\">(s1, s2) -&gt;<\/p>\n<p>{ return s1.trim().compareTo( s2.trim() ); }<\/td>\n<td valign=\"top\" width=\"306\">(s1, s2) -&gt;<\/p>\n<p>s1.trim().compareTo( s2.trim() )<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"306\">(a, b) -&gt; { return a + b; }<\/td>\n<td valign=\"top\" width=\"306\">(a, b) -&gt; a + b<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"306\">() -&gt; { System.out.println(); }<\/td>\n<td valign=\"top\" width=\"306\">() -&gt; System.out.println()<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Ausf\u00fchrliche und abgek\u00fcrzte Schreibweise<\/p>\n<p>Ausdr\u00fccke k\u00f6nnen in Java auch zu void ausgewertet werden, sodass ohne Probleme ein Aufruf wie\u00a0 System.out.println() in der kompakten Schreibweise ohne Block gesetzt werden kann. Das hei\u00dft, wenn Lambda-Ausdr\u00fccke mit der kurzen Ausdrucks-Syntax eingesetzt werden, k\u00f6nnen diese Ausdr\u00fccke etwas zur\u00fcckgeben, m\u00fcssen aber nicht.<\/p>\n<p>Hinweis<\/p>\n<p>Die Schreibweise mit den geschweiften Klammern und den R\u00fcckgabe-Ausdr\u00fccken kann nicht gemischt werden. Entweder gibt es ein Block geschweifter Klammern und return oder keine Klammern und kein return-Schl\u00fcsselwort. Fehler ergeben also diese falschen Mischungen:<\/p>\n<pre>Comparator&lt;String&gt; c;\r\nc = (s1, s2) -&gt; <b>{<\/b> s1.trim().compareTo( s2.trim() ) <b>}<\/b>;\u00a0\u00a0\u00a0 \/\/ Compilerfehler (1)\r\nc = (s1, s2) -&gt; <b>return<\/b> s1.trim().compareTo( s2.trim() ); \/\/ Compilerfehler (2)<\/pre>\n<p>W\u00fcrden wir in (1) ein explizites return nutzen w\u00e4re alles in Ordnung, w\u00fcrde bei (2) das return wegfallen w\u00e4re die Zeile auch compilierbar.<\/p>\n<p>Ob Lambda-Ausdr\u00fccke eine R\u00fcckgabe haben, dr\u00fccken zwei Begriffe aus:<\/p>\n<ul>\n<li>void-kompatibel: Der Lambda-Rumpf gibt kein Ergebnis zur\u00fcck. Entweder weil der Block kein return enth\u00e4lt, oder ein return ohne R\u00fcckgabe, oder weil ein void-Ausdruck in der verk\u00fcrzten Schreibweise eingesetzt wird. Der Lambda-Ausdruck () -&gt; System.out.println() ist also void-kompatibel, genauso wie () -&gt; {}.<\/li>\n<li>Wert-kompatibel: Der Rumpf beendet den Lambda-Ausdruck mit einer return-Anweisung, die einen Wert zur\u00fcckgibt oder besteht aus der kompakten Schreibenweise mit einer R\u00fcckgabe ungleich void.<\/li>\n<\/ul>\n<p>Eine Mischung aus void- und Wert-kompatibel ist nicht erlaubt und f\u00fchrt wie bei Methoden zu einem Compilerfehler.<a title=\"\" href=\"file:\/\/\/C:\/Users\/Christian\/Dropbox\/Eigene%20Dokumente\/Insel\/todo\/1.Lambdas,%20JPB,%20ISP,%20JM,%20zu%20MS.doc#_ftn1\">[1]<\/a><\/p>\n<h4>Abk\u00fcrzung 3: Einzelner Identifizierer statt Parameterliste und Klammern<\/h4>\n<p>Besteht die Parameterliste<\/p>\n<p>a) nur aus einem einzelnen Identifizierer und<\/p>\n<p>b) ist der Typ durch Typ-Inferenz klar,<\/p>\n<p>k\u00f6nnen die runden Klammern wegfallen.<\/p>\n<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"205\">Lange Schreibweise<\/td>\n<td valign=\"top\" width=\"205\">Typen inferred<\/td>\n<td valign=\"top\" width=\"206\">Vollst\u00e4ndig abgek\u00fcrzt<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"205\">(String s) -&gt; s.length()<\/td>\n<td valign=\"top\" width=\"205\">(s) -&gt; s.length()<\/td>\n<td valign=\"top\" width=\"206\">s -&gt; s.length()<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"205\">(int i) -&gt; Math.abs( i )<\/td>\n<td valign=\"top\" width=\"205\">(i) -&gt; Math.abs( i )<\/td>\n<td valign=\"top\" width=\"206\">i -&gt; Math.abs( i )<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Unterschiedlicher Grad von Abk\u00fcrzungen<\/p>\n<p>Kommen alle Abk\u00fcrzungen zusammen, l\u00e4sst sich etwa die H\u00e4lfte an Code einsparen. Aus (int i) -&gt; { return Math.abs( i ); } wird einfach i -&gt; Math.abs( i ).<\/p>\n<p>Syntax-Hinweis<\/p>\n<p>Nur bei genau einem Lambda-Parameter k\u00f6nnen die Klammern weggelassen werden, da es sonst Mehrdeutigkeiten gibt, f\u00fcr die es sonst wieder komplexe Regeln zur Aufl\u00f6sung geben m\u00fcsste. Hei\u00dft es etwa foo( k, v -&gt; { \u2026 } ) ist es unklar, ob foo zwei Parameter deklariert. Ist das zweite Argument ein Lambda-Ausdruck, oder handelt es sich um nur genau einen Parameter, wobei dann ein Lambda-Ausdruck \u00fcbergeben wird, der selbst zwei Parameter deklariert. Um Probleme wie diesen aus dem Weg zu gehen, k\u00f6nnen Entwickler auf den ersten Blick sehen, dass foo( k, v -&gt; { \u2026 } ) eindeutig f\u00fcr Parameter steht, und foo( (k, v) -&gt; { \u2026 } ) nur einen Parameter besitzt.<\/p>\n<h4><\/h4>\n<div><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Lambda-Ausdr\u00fccke haben wie Methoden m\u00f6gliche Parameter- und R\u00fcckgabe-Werte. Die Java-Grammatik f\u00fcr die Schreibweise von Lambda-Ausdr\u00fccken sieht ein paar n\u00fctzliche syntaktische Abk\u00fcrzungen vor. Ausf\u00fchrliche Schreibweise Lambda-Ausdr\u00fccke lassen sich auf unterschiedliche Arten und Weisen schreiben, da es f\u00fcr diverse Konstruktionen Abk\u00fcrzungen gibt. Eine Form, die jedoch immer gilt ist: &#8218;(&#8218; LambdaParameter &#8218;)&#8216; &#8218;-&gt;&#8216; &#8218;{&#8218; Anweisungen &#8218;}&#8216; Der [&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-2731","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\/2731","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=2731"}],"version-history":[{"count":2,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/2731\/revisions"}],"predecessor-version":[{"id":3890,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/2731\/revisions\/3890"}],"wp:attachment":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/media?parent=2731"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/categories?post=2731"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/tags?post=2731"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}