{"id":2736,"date":"2014-03-21T12:01:14","date_gmt":"2014-03-21T10:01:14","guid":{"rendered":"http:\/\/www.tutego.de\/blog\/javainsel\/?p=2736"},"modified":"2014-03-21T12:01:14","modified_gmt":"2014-03-21T10:01:14","slug":"java-8-ausnahme-lambda-ausdruecke","status":"publish","type":"post","link":"https:\/\/www.tutego.de\/blog\/javainsel\/2014\/03\/java-8-ausnahme-lambda-ausdruecke\/","title":{"rendered":"Ausnahmen in Lambda-Ausdr\u00fccken"},"content":{"rendered":"<p>Lambda-Ausdr\u00fccke sind Implementierung von funktionalen Schnittstellen, und bisher haben wir noch nicht die Frage betrachtet was passiert, wenn der Code-Block vom Lambda-Ausdruck eine Ausnahme ausl\u00f6st und wer diese Auffangen muss.<\/p>\n<h4>Ausnahmen im Code-Block eines Lambda-Ausdrucks<\/h4>\n<p>In java.util.function gibt es eine funktionale Schnittstelle Predicate, dessen Deklaration im Kern wie folgt ist:<\/p>\n<pre>public interface Predicate&lt;T&gt; { boolean test( T t ); }<\/pre>\n<p>Ein Predicate f\u00fchrt einen Test durch und liefert wahr oder falsch als Ergebnis. Ein Lambda-Ausdruck kann diese Schnittstelle nun implementieren. Nehmen wir an, wir wollen Testen, ob eine Datei die L\u00e4nge 0 hat, um etwa Datei-Leichen zu finden. In einer ersten Idee greifen wir auf die existierende Files-Klasse zur\u00fcck, die size(\u2026)anbietet:<\/p>\n<pre>Predicate&lt;Path&gt; isEmptyFile = path -&gt; Files.size( path ) == 0;\u00a0 \/\/ N Compilerfehler<\/pre>\n<p>Problem dabei ist, das Files.size(\u2026) eine IOException ausl\u00f6st, die behandelt werden muss und zwar <i>nicht<\/i> vom Block, in dem der Lambda-Ausdruck als Ganzes steht, sondern vom Code im Lambda-Ausdruck selbst. Das schreibt der Compiler so vor. Folgendes ist also keine L\u00f6sung:<b><\/b><\/p>\n<pre><b>try {\r\n<\/b>  Predicate&lt;Path&gt; isEmptyFile = path -&gt; Files.size( path ) == 0; \/\/ Nee\r\n<b>} catch ( IOException e ) { \u2026 }<\/b><\/pre>\n<p>sondern nur:<\/p>\n<pre>Predicate&lt;Path&gt; isEmptyFile = path -&gt; {\r\n<b>\u00a0 <\/b><b>try {\r\n<\/b>    return Files.size( path ) == 0;\r\n<b>\u00a0 <\/b><b>} catch ( IOException e ) { return false; }\r\n<\/b>};<\/pre>\n<p>Die Eigenschaft, die Java fehlt, nennt sich Exception-Transparenz, und hier ist deutlich der Unterschied zwischen gepr\u00fcften und ungepr\u00fcften Ausnahmen zu sehen. Bei der Exception-Transparenz w\u00e4re kein Ausnahmebehandlung im Lambda-Ausdruck n\u00f6tig und an einer \u00fcbergeordneten Stelle m\u00f6glich. Doch da diese M\u00f6glichkeit in Java fehlt, bleibt uns nur \u00fcbrig, gepr\u00fcfte Ausnahmen im Lambda-Ausdr\u00fccken direkt zu behandeln.<\/p>\n<h4>Funktionale Schnittstellen mit throws-Klausel<\/h4>\n<p>Ungepr\u00fcfte Ausnahmen k\u00f6nnen immer auftreten und f\u00fchren (nicht abgefangen) wie \u00fcblich zum Abbruch des Threads. Eine throws-Klausel an den Methoden\/Konstruktoren ist daf\u00fcr nicht n\u00f6tig. Doch k\u00f6nnen Funktionale Schnittstellen eine throws-Klausel mit gepr\u00fcften Ausnahmen deklarieren, und die Implementierung einer funktionalen Schnittstelle kann logischerweise gepr\u00fcfte Ausnahmen ausl\u00f6sen.<\/p>\n<p>Eine Deklaration wie Callable aus dem Paket java.util.concurrent macht das deutlich. (Callable tr\u00e4gt kein @FunctionalInterface):<\/p>\n<pre>public interface Callable&lt;V&gt; {\r\n  V call() <b>throws Exception<\/b>;\r\n}<\/pre>\n<p>Das k\u00f6nnte durch folgenden Lambda-Ausdruck realisiert werden:<\/p>\n<pre>Callable&lt;Integer&gt; randomDice = () -&gt; (int)(Math.random() * 6) + 1;<\/pre>\n<p>Der Aufruf von call() auf einem randomDice muss mit einer Ausnahmebehandlung einher gehen, da call() eine Exception ausl\u00f6st, etwa so:<\/p>\n<pre>try {\r\n  System.out.println( randomDice.call() );\r\n  System.out.println( randomDice.call() );\r\n}\r\ncatch ( Exception e ) { \u2026 }<\/pre>\n<p>Dass der Aufrufer die Ausnahme behandeln muss ist klar. Die Deklaration des Lambda-Ausdrucks enth\u00e4lt keinen Hinweis auf die Ausnahme, das ist ein Unterschied zum vorangegangenen Abschnitt.<\/p>\n<p>Design-Tipp<\/p>\n<p>Ausnahmen in Methoden funktionaler Schnittstellen schr\u00e4nken den Nutzen stark ein, und daher l\u00f6st keine der funktionalen Schnittstellen aus etwa java.util.function eine gepr\u00fcfte Ausnahme aus. Der Grund ist einfach, denn jeder Methodenaufrufer m\u00fcsste sonst entweder die Ausnahme weiterleiten oder behandeln.<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<p>Um die Einschr\u00e4nkungen und Probleme mit einer throws-Klausel\u00a0 noch etwas deutlicher zu machen stellen wir uns vor, dass die funktionale Schnittstelle Predicate ein throws Exception (vom Sinn der Typs Exception an sich einmal abgesehen) enth\u00e4lt:<\/p>\n<pre>interface Predicate&lt;T&gt; { boolean test( T t ) <b>throws Exception<\/b>; } \/\/ Was w\u00e4re wenn?<\/pre>\n<p>Die Konsequenz w\u00e4re, dass jeder Aurufer von test(\u2026) nun seinerseits die Exception in die H\u00e4nde bekommt und sie auffangen oder weiterleiten muss. Leitet der test(\u2026.)-Aufrufer mit throws Exception die Ausnahme weiter nach oben, bekommen wir pl\u00f6tzlich an allen Stellen ein throws Exception in die Methodensignatur, was auf keinen Fall gew\u00fcnscht ist. So enth\u00e4lt jetzt etwa ArrayList eine Deklaration von removeIf(Predicate filter); hier m\u00fcsste dann removeIf(\u2026) \u2013 was letztendlich filter.test(\u2026) aufruft \u2013 sich mit der Test-Ausnahme rum\u00e4rgern und removeIf(Predicate filter) throws Exception ist keine gute Sache.<\/p>\n<h4>Von gepr\u00fcft nach ungepr\u00fcft<\/h4>\n<p>Gepr\u00fcfte Ausnahmen sind in Lamba-Ausdr\u00fccken nicht sch\u00f6n. Eine L\u00f6sung ist, Code, der gepr\u00fcfte Ausnahmen ausl\u00f6st, zu verpacken und die gepr\u00fcfte Ausnahme in einer ungepr\u00fcften zu manteln. Das kann etwa so aussehen:<\/p>\n<pre>public class PredicateWithException {\r\n\r\n @FunctionalInterface\r\n public interface ExceptionalPredicate&lt;T, E extends Exception&gt; {\r\n   boolean test( T t ) throws E;\r\n }\r\n\r\n public static &lt;T&gt; Predicate&lt;T&gt; asUncheckedPredicate( ExceptionalPredicate&lt;T, Exception&gt; predicate ) {\r\n  return t -&gt; {\r\n   try {\r\n    return predicate.test( t );\r\n   }\r\n   catch ( Exception e ) {\r\n    throw new RuntimeException( e.getMessage(), e );\r\n   }\r\n  };\r\n }\r\n public static void main( String[] args ) {\r\n  Predicate&lt;Path&gt; isEmptyFile = asUncheckedPredicate( path -&gt; Files.size( path ) == 0 );\r\n  System.out.println( isEmptyFile.test( Paths.get( \"c:\/\" ) ) );\r\n }\r\n}<\/pre>\n<p>Die Schnittstelle ExceptionalPredicate ist ein Pr\u00e4dikat mit optionaler Ausnahme. In der eigenen Hilfsmethode asUncheckedPredicate(ExceptionalPredicate) nehmen wir so ein ExceptionalPredicate an und packen es in ein Predicate, was die Methode zur\u00fcckgibt. Gepr\u00fcfte Ausnahmen werden in eine ungepr\u00fcfte Ausnahme vom Typ RuntimeException gesetzt. Somit muss Predicate keine gepr\u00fcfte Ausnahme weiterleiten, was es ja laut Deklaration auch nicht kann.<\/p>\n<div>\n<hr align=\"left\" size=\"1\" width=\"33%\" \/>\n<div>\n<p><a title=\"\" href=\"file:\/\/\/C:\/Users\/Christian\/Dropbox\/Eigene%20Dokumente\/Insel\/todo\/1.Lambdas,%20JPB,%20ISP,%20JM,%20zu%20MS.doc#_ftnref1\">[1]<\/a> \u00a0\u00a0\u00a0\u00a0\u00a0 Von Callable gibt es zwar Nutzer, die mit Nebenl\u00e4ufigkeit (daher das Paket java.util.concurrent) in Zusammenhang stehen, aber keine weiteren Verwendungen in der Java-Bibliothek, von zwei Beispielen aus javax.tools abgesehen. Mit java.util.function.Supplier existiert eine entsprechende Alternative ohne throws-Klausel.<\/p>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Lambda-Ausdr\u00fccke sind Implementierung von funktionalen Schnittstellen, und bisher haben wir noch nicht die Frage betrachtet was passiert, wenn der Code-Block vom Lambda-Ausdruck eine Ausnahme ausl\u00f6st und wer diese Auffangen muss. Ausnahmen im Code-Block eines Lambda-Ausdrucks In java.util.function gibt es eine funktionale Schnittstelle Predicate, dessen Deklaration im Kern wie folgt ist: public interface Predicate&lt;T&gt; { boolean [&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-2736","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\/2736","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=2736"}],"version-history":[{"count":1,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/2736\/revisions"}],"predecessor-version":[{"id":2737,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/2736\/revisions\/2737"}],"wp:attachment":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/media?parent=2736"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/categories?post=2736"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/tags?post=2736"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}