{"id":2756,"date":"2014-03-27T10:09:57","date_gmt":"2014-03-27T08:09:57","guid":{"rendered":"http:\/\/www.tutego.de\/blog\/javainsel\/?p=2756"},"modified":"2014-03-27T10:09:57","modified_gmt":"2014-03-27T08:09:57","slug":"funktionale-programmierung-mit-java","status":"publish","type":"post","link":"https:\/\/www.tutego.de\/blog\/javainsel\/2014\/03\/funktionale-programmierung-mit-java\/","title":{"rendered":"Funktionale Programmierung mit Java"},"content":{"rendered":"<h3>Programmierparadigmen: imperativ oder deklarativ<\/h3>\n<p>In irgendeiner Weise muss ein Entwickler sein Problem in Programmform beschreiben, damit der Computer es letztendlich ausf\u00fchren kann. Hier gibt es verschiedene Beschreibungsformen, die wir Programmierparadigmen\u00a0nennen. Bisher haben wir uns immer mit der imperativen Programmierung besch\u00e4ftigt, bei der Anweisungen im\u00a0 Mittelpunkt stehen. Wir haben im Deutschen den Imperativ, also die Befehlsform, die sehr gut mit dem Programmierstil vergleichbar ist, denn es handelt sich in beiden F\u00e4llen um Anweisungen der Art \u201etue dies, tue das\u201c. Diese \u201eBefehle\u201c mit Variablen, Fallunterscheidungen, Spr\u00fcngen beschreiben das Programm und den L\u00f6sungsweg.<\/p>\n<p>Zwar ist imperative Programmierung die technisch \u00e4lteste, aber nicht die einzige Form Programme zu beschreiben; es gibt daneben die deklarative Programmierung, die nicht das \u201ewie\u201c zur Probleml\u00f6sung beschreibt, sondern das \u201ewas\u201c, also was eigentlich gefordert ist ohne sich in genauen Abl\u00e4ufen zu verstricken. Auf den ersten Blick klingt das abstrakt, aber f\u00fcr jeden, der schon einmal<\/p>\n<ul>\n<li>einen Selektion wie <i>*.html<\/i> auf der Kommandozeile\/im Explorer-Suchefeld get\u00e4tigt,<\/li>\n<li>eine Datenbankanfrage mit SQL geschrieben,<\/li>\n<li>eine XML-Selektion mit XQuery genutzt,<\/li>\n<li>ein Build-Skript mit Ant oder make formuliert,<\/li>\n<li>eine XML-Transformation mit XSLT beschrieben hat<\/li>\n<\/ul>\n<p>wird das Prinzip kennen.<\/p>\n<p>Bleiben wir kurz bei SQL, um einen Punkt deutlich zu machen. Nat\u00fcrlich ist im Endeffekt die Abarbeitung der Tabellen und Auswertungen der Ergebnisse von der CPU rein imperativ, doch es geht um die Programmbeschreibung auf einem h\u00f6heren Abstraktionsniveau. Deklarative Programme sind \u00fcblicherweise wesentlicher k\u00fcrzer und damit kommen weitere Vorteile wie leichtere Erweiterbarkeit, Verst\u00e4ndlichkeit ins Spiel. Da deklarative Programme oftmals einen mathematischen Hintergrund haben, lassen sich die Beschreibungen leichter formal in ihrer Korrektheit beweisen.<\/p>\n<p>Deklarative Programmierung ist ein Programmierstil, und eine deklarative Beschreibung braucht eine Art \u201eAblaufumgebung\u201c, denn SQL kann zum Beispiel keine CPU direkt ausf\u00fchren. Aber statt nur spezielle Anwendungsf\u00e4lle wie Datenbank- oder XML-Abfragen zu behandeln, k\u00f6nnen auch typische Algorithmen deklarativ formuliert werden, und zwar mit funktionaler Programmierung. Damit sind imperative Programme und funktionale Programme gleich m\u00e4chtig in ihren M\u00f6glichkeiten.<\/p>\n<h3>Funktionale Programmierung und funktionale Programmiersprachen<\/h3>\n<p>Bei der funktionalen Programmierung stehen Funktionen im Mittelpunkt und ein im Idealfall zustandsloses Verhalten, in dem viel mit Rekursion gearbeitet wird. Ein typisches Beispiel ist die Berechung der Fakult\u00e4t. Es ist n! = 1 \u00b7 2 \u00b7 3 \u00b7 &#8230; \u00b7 n, und mit Schleifen und Variablen, dem imperativen Weg, sieht sie so aus:<\/p>\n<pre>public static int factorial( int n ) {\r\n\u00a0 int result = 1;\r\n\u00a0 for ( int i = 1; i &lt;= n; i++ )\r\n\u00a0\u00a0\u00a0 result *= i;\r\n\u00a0 return result;\r\n}<\/pre>\n<p>Deutlich sind die vielen Zuweisungen und die Fallunterscheidung durch die Schleife abzulesen; die typischen Indikatoren f\u00fcr imperative Programme. Der Schleifenz\u00e4hler erh\u00f6ht sich, damit kommt Zustand in das Programm, denn der aktuelle Index muss ja irgendwo im Speicher gehalten werden. Bei der rekursiven Variante ist das ganz anders, hier gibt es keine Zuweisungen im Programm und die Schreibweise erinnert an die mathematische Definition:<\/p>\n<pre>public static int factorial( int n ) {\r\n\u00a0 return n == 0 ? 1 : n * factorial( n - 1 );\r\n}<\/pre>\n<p>Mit der funktionalen Programmierung haben wir eine echte Alternative zur imperativen Programmierung. Die Frage ist nur: Mit welcher Programmiersprache lassen sich funktionale Programme schreiben? Im Grunde mit jeder h\u00f6heren Programmiersprache! Denn funktional zu programmieren ist ja ein Programmierstil, und Java unterst\u00fctzt funktionale Programmierung, wie wir am Beispiel mit der Fakult\u00e4t ablesen k\u00f6nnen. Da das im Prinzip schon alles ist, stellt sich die Frage, warum funktionale Programmierung einen so schweren Stand hat und bei den Entwicklern gef\u00fcrchtet ist. Das hat mehrere Gr\u00fcnde:<\/p>\n<p><b>Lesbarkeit.<\/b> Am Anfang der funktionalen Programmiersprachen steht historisch LISP aus dem Jahr 1958, eine sehr flexible, aber ungewohnt zu lesende Programmiersprache. Unsere Fakult\u00e4t sieht in LISP so aus:<\/p>\n<pre>(defun factorial (n) (if (= n 1) 1 (* n (factorial (- n 1)))))<\/pre>\n<p>Die ganzen Klammern machen die Programme nicht einfach lesbar und die Ausdr\u00fccke stehen in der Pr\u00e4fix-Notation &#8211; n 1 statt der \u00fcblichen Infix-Notation n &#8211; 1. Bei anderen funktionalen Programmiersprachen ist es anders, dennoch f\u00fchrt das zu einem gewissen Vorurteil, dass alle funktionalen Programmiersprachen schlecht lesbar sind.<\/p>\n<p><b>Performance und Speicherverbrauch<\/b>. Ohne clevere Optimierungen von Seiten des Compilers und der Laufzeitumgebung f\u00fchren insbesondere rekursive Aufrufe zu prall gef\u00fcllten Stacks und schlechter Laufzeit.<\/p>\n<p><b>Rein funktional<\/b>. Es gibt funktionale Programmiersprachen, die als \u201erein\u201c oder \u201epur\u201c bezeichnet werden und keine Zustands\u00e4nderungen erlauben. Die Entwicklung von Ein-\/Ausgabeoperationen oder simplen Zufallszahlen ist ein gro\u00dfer Akt, der f\u00fcr normale Entwickler nicht mehr nachvollziehbar ist. Die Konzepte sind kompliziert, doch zum Gl\u00fcck sind die meisten funktionalen Sprachen nicht so rein und erlauben Zustands\u00e4nderungen, nur Programmierer versuchen genau diese Zustand\u00e4nderungen zu vermeiden, um sich nicht die Nachteile damit einzuhandeln.<\/p>\n<p><b>Funktional mit Java.<\/b> Wenn es darum geht nur mit Funktionen zu arbeiten, kommen Entwickler schnell zu einem Punkt, an dem Funktionen andere Funktionen als Argumente \u00fcbergeben oder Funktionen zur\u00fcckgeben. So etwas l\u00e4sst sich in Java in der traditionellen Syntax nur sehr umst\u00e4ndlich schreiben. Dies f\u00fchrt dazu, dass alles so unlesbar wird, dass der ganze Vorteil der kompakten deklarativen Schreibweise verloren geht.<b><\/b><\/p>\n<p>Aus heutiger Sicht stellt sich eine Kombination aus beiden Konzepten als zukunftsweisend dar. Mit der in Java\u00a08 eingef\u00fchrten Schreibweise der Lambda-Ausdr\u00fccke sind funktionale Programme kompakt und relativ gut lesbar und die JVM hat gute Optimierungsm\u00f6glichkeiten. Java erm\u00f6glicht beide Programmierparadigmen und Entwickler k\u00f6nnen den Weg w\u00e4hlen, der f\u00fcr eine Probleml\u00f6sung gerade am Besten ist. Diese Mehrdeutigkeit schafft nat\u00fcrlich auch Probleme, denn immer wenn es mehrere L\u00f6sungswege gibt, entstehen Auseinandersetzungen um die Beste der Varianten \u2013 und hier kann von Entwickler zu Entwickler eine kontr\u00e4re Meinung herrschen. Funktionale Programmierung hat unbestrittene Vorteile und das wollen wir uns genau anschauen.<\/p>\n<h3>Funktionale Programmierung in Java am Beispiel vom Comparator<\/h3>\n<p>Funktionale Programmierung hat auch daher etwas akademisches, weil in den K\u00f6pfen der Entwickler oftmals dieses Programmierparadigma nur mit mathematischen Funktionen in Verbindung gebracht wird. Und die wenigsten werden tats\u00e4chlich Fakult\u00e4t oder Fibonacci-Zahlen in Programmen ben\u00f6tigen und daher schnell funktionale Programmierung beiseite legen. Doch diese Vorurteile sind unbegr\u00fcndet, und es ist hilfreich, funktionale Programmierung gedanklich von der Mathematik zu l\u00f6sen, denn die allermeisten Programme haben nichts mit mathematischen Funktionen im eigentlichen Sinne zu tun, wohl aber viel st\u00e4rker mit formal beschriebenen Methoden.<\/p>\n<p>Betrachten wir erneut unser Beispiel aus der Einleitung, die Sortierung von Strings, diesmal aus der Sicht eines funktionalen Programmierers. Ein Comparator ist eine einfache \u201eFunktion\u201c, mit zwei Parametern und einer R\u00fcckgabe. Diese \u201eFunktion\u201c (realisiert als Methode) wiederum wird an die sort(\u2026)-Methode \u00fcbergeben. Alles das ist funktionale Programmierung, denn wir programmieren Funktionen und \u00fcbergeben sie. Drei Beispiele (Generics ausgelassen):<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"382\"><b>Code<\/b><\/td>\n<td valign=\"top\" width=\"382\"><b>Bedeutung<\/b><\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"382\">Comparator c = (c1, c2) -&gt; &#8230;<\/td>\n<td valign=\"top\" width=\"382\">Implementiert eine Funktion \u00fcber Lambda-Ausdruck<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"382\">Arrays.sort(T[] a, Comparator c)<\/td>\n<td valign=\"top\" width=\"382\">Nimmt eine Funktion als Argument an<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"382\">Collections.reverseOrder(Comparator cmp)<\/td>\n<td valign=\"top\" width=\"382\">Nimmt eine Funktion an und liefert auch eine zur\u00fcck<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><em>Beispiele f\u00fcr Funktionen in der \u00dcbergabe und als R\u00fcckgabe<\/em><\/p>\n<p>Funktionen selbst k\u00f6nnen in Java nicht \u00fcbergeben werden, also helfen sich Java-Entwickler mit der M\u00f6glichkeit, die Funktionalit\u00e4t in eine Methode zu setzen, sodass die Funktion zum Objekt mit einer Methode wird, was die Logik realisiert. Lambda-Ausdr\u00fccke bzw. Methoden\/Konstruktor-Referenzen geben eine kompakte Syntax ohne den Ballast, extra eine Klasse mit einer Methoden schreiben zu m\u00fcssen.<\/p>\n<p>Der Typ Comparator ist eine funktionale Schnittstelle und steht f\u00fcr eine besondere Funktion mit zwei Parametern gleichen Typs und einer Ganzzahl-R\u00fcckgabe. Es gibt weitere funktionale Schnittstellen, die etwas flexibler sind als Comparator, in der Weise, dass etwa die R\u00fcckgabe statt int auch double oder etwas anderes sein k\u00f6nnen.<\/p>\n<h3>Lambda-Ausdr\u00fccke als Funktionen sehen<\/h3>\n<p>Wir haben gesehen, dass sich Lambda-Ausdr\u00fccke in einer Syntax formulieren lassen, die folgende allgemeine Form hat:<\/p>\n<p>&#8218;(&#8218; <i>LambdaParameter<\/i> &#8218;)&#8216; &#8218;-&gt;&#8216; &#8218;{&#8218; <i>Anweisungen<\/i> &#8218;}&#8216;<\/p>\n<p>Der Pfeil macht gut deutlich, dass wir es bei Lambda-Ausdr\u00fccken mit Funktionen zu tun haben, die etwas abbilden. Im Fall vom Comparator ist es eine Abbildung von zwei Strings auf eine Ganzzahl; in einer etwas mathematischeren Notation gepackt: (String, String) \u2192 int.<\/p>\n<p><b>Beispiel<\/b> Methoden gibt es mit und ohne R\u00fcckgabe und mit und ohne Parameter. Genauso ist das mit\u00a0 Lambda-Ausdr\u00fccken. Ein paar Beispiele in Java-Code mit ihren Abbildungen.<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"382\"><b>Lambda-Ausdruck<\/b><\/td>\n<td valign=\"top\" width=\"382\"><b>Abbildung<\/b><\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"382\">(int a, int b) -&gt; a + b<\/td>\n<td valign=\"top\" width=\"382\">(int, int) \u2192 int<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"382\">(int a) -&gt; Math.abs( a )<\/td>\n<td valign=\"top\" width=\"382\">(int) \u2192 int<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"382\">(String s) -&gt; s.isEmpty()<\/td>\n<td valign=\"top\" width=\"382\">(String) \u2192 boolean<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"382\">(Collection c) -&gt; c.size()<\/td>\n<td valign=\"top\" width=\"382\">(Collection) \u2192 int<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"382\">() -&gt; Math.random()<\/td>\n<td valign=\"top\" width=\"382\">() \u2192 double<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"382\">(String s) -&gt; { System.out.print( s ); }<\/td>\n<td valign=\"top\" width=\"382\">(String) \u2192 void<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"382\">() -&gt; {}<\/td>\n<td valign=\"top\" width=\"382\">() \u2192 void<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><em>Lambda-Ausdr\u00fccke und was sie als Funktionen abbilden<\/em><\/p>\n<p>Begriff: Funktion vs. Methode.\u00a0Die Java Sprachdefinition kennt den Begriff \u201eFunktion\u201c nicht, sondern spricht nur von Methoden. Methoden h\u00e4ngen immer an Klassen und das hei\u00dft, dass Methoden immer an einem Kontext h\u00e4ngen. Das ist zentral bei der Objektorientierung, da Methoden auf Attribute lesend und schreibend zugreifen k\u00f6nnen. Lambda-Ausdr\u00fccke wiederum realisieren Funktion, die erst einmal ihre Arbeitswerte rein aus den Parametern beziehen, sie h\u00e4ngen nicht an Klassen und Objekten. Der Gedanke bei funktionalen Programmiersprachen ist der, ohne Zust\u00e4nde auszukommen, also Funktionen so clever anzuwenden, dass sie ein Ergebnis liefern. Funktionen geben f\u00fcr eine spezifische Parameterkombination immer dasselbe Ergebnis zur\u00fcck, unabh\u00e4ngig vom Zustand des umgebenden Gesamtprogramms.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Programmierparadigmen: imperativ oder deklarativ In irgendeiner Weise muss ein Entwickler sein Problem in Programmform beschreiben, damit der Computer es letztendlich ausf\u00fchren kann. Hier gibt es verschiedene Beschreibungsformen, die wir Programmierparadigmen\u00a0nennen. Bisher haben wir uns immer mit der imperativen Programmierung besch\u00e4ftigt, bei der Anweisungen im\u00a0 Mittelpunkt stehen. Wir haben im Deutschen den Imperativ, also die Befehlsform, [&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-2756","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\/2756","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=2756"}],"version-history":[{"count":1,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/2756\/revisions"}],"predecessor-version":[{"id":2757,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/2756\/revisions\/2757"}],"wp:attachment":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/media?parent=2756"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/categories?post=2756"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/tags?post=2756"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}