---
title: 'Ausnahmen in Lambda-Ausdrücken'
url: 'https://www.tutego.de/blog/javainsel/2014/03/java-8-ausnahme-lambda-ausdruecke'
markdown: 'https://www.tutego.de/blog/javainsel/2014/03/java-8-ausnahme-lambda-ausdruecke.md'
date: '2014-03-21'
description: 'Lambda-Ausdrücke 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öst und wer diese Auffangen muss. Ausnahmen im Code-Block eines Lambda-Ausdrucks In java.util.function …'
taxonomy:
  category:
    - Insel
    - 'Java 8'
---

# Ausnahmen in Lambda-Ausdrücken

![Illustration einer tropischen Insel mit Palmen und einem hohen Berg](https://www.tutego.de/blog/javainsel/user/themes/tutego/images/insel-cover.jpg)# Java Blog für Programmierer

[Java Blog für Programmierer](https://www.tutego.de/blog/javainsel/)

## Ausnahmen in Lambda-Ausdrücken

21.03.2014 · [Christian Ullenboom](https://www.tutego.de/blog/javainsel/author/admin/)

Lambda-Ausdrücke 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öst 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:

```java
public interface Predicate { boolean test( T t ); }
```

Ein `Predicate` führt 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änge 0 hat, um etwa Datei-Leichen zu finden. In einer ersten Idee greifen wir auf die existierende `Files`-Klasse zurück, die `size(…)`anbietet:

```java
Predicate isEmptyFile = path -> Files.size( path ) == 0;  // N Compilerfehler
```

Problem dabei ist, das `Files.size(…)` eine `IOException` auslöst, die behandelt werden muss und zwar *nicht* 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ösung:

```java
try {
  Predicate isEmptyFile = path -> Files.size( path ) == 0; // Nee
} catch ( IOException e ) { ... }
```

sondern nur:

```java
Predicate isEmptyFile = path -> {
  try {
    return Files.size( path ) == 0;
  } catch ( IOException e ) { return false; }
};
```

Die Eigenschaft, die Java fehlt, nennt sich `Exception`-Transparenz, und hier ist deutlich der Unterschied zwischen geprüften und ungeprüften Ausnahmen zu sehen. Bei der `Exception`-Transparenz wäre kein Ausnahmebehandlung im Lambda-Ausdruck nötig und an einer übergeordneten Stelle möglich. Doch da diese Möglichkeit in Java fehlt, bleibt uns nur übrig, geprüfte Ausnahmen im Lambda-Ausdrücken direkt zu behandeln.

#### Funktionale Schnittstellen mit throws-Klausel

Ungeprüfte Ausnahmen können immer auftreten und führen (nicht abgefangen) wie üblich zum Abbruch des Threads. Eine throws-Klausel an den Methoden/Konstruktoren ist dafür nicht nötig. Doch können Funktionale Schnittstellen eine throws-Klausel mit geprüften Ausnahmen deklarieren, und die Implementierung einer funktionalen Schnittstelle kann logischerweise geprüfte Ausnahmen auslösen.

Eine Deklaration wie `Callable` aus dem Paket `java.util.concurrent` macht das deutlich. (`Callable` trägt kein `@FunctionalInterface`):

```java
public interface Callable {
  V call() throws Exception;
}
```

Das könnte durch folgenden Lambda-Ausdruck realisiert werden:

```java
Callable randomDice = () -> (int)(Math.random() * 6) + 1;
```

Der Aufruf von `call()` auf einem `randomDice` muss mit einer Ausnahmebehandlung einher gehen, da `call()` eine `Exception` auslöst, etwa so:

```java
try {
  System.out.println( randomDice.call() );
  System.out.println( randomDice.call() );
}
catch ( Exception e ) { ... }
```

Dass der Aufrufer die Ausnahme behandeln muss ist klar. Die Deklaration des Lambda-Ausdrucks enthält keinen Hinweis auf die Ausnahme, das ist ein Unterschied zum vorangegangenen Abschnitt.

Design-Tipp

Ausnahmen in Methoden funktionaler Schnittstellen schränken den Nutzen stark ein, und daher löst keine der funktionalen Schnittstellen aus etwa `java.util.function` eine geprüfte Ausnahme aus. Der Grund ist einfach, denn jeder Methodenaufrufer müsste sonst entweder die Ausnahme weiterleiten oder behandeln.[\[1\]](file:C:/Users/Christian/Dropbox/Eigene%20Dokumente/Insel/todo/1.Lambdas,%20JPB,%20ISP,%20JM,%20zu%20MS.doc#_ftn1)

Um die Einschränkungen und Probleme mit einer throws-Klausel 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ält:

```java
interface Predicate { boolean test( T t ) throws Exception; } // Was wäre wenn?
```

Die Konsequenz wäre, dass jeder Aurufer von `test(…)` nun seinerseits die `Exception` in die Hände bekommt und sie auffangen oder weiterleiten muss. Leitet der test(….)-Aufrufer mit throws `Exception` die Ausnahme weiter nach oben, bekommen wir plötzlich an allen Stellen ein throws `Exception` in die Methodensignatur, was auf keinen Fall gewünscht ist. So enthält jetzt etwa `ArrayList` eine Deklaration von `removeIf(Predicate filter)`; hier müsste dann `removeIf(…)` – was letztendlich `filter.test(…)` aufruft – sich mit der Test-Ausnahme rumärgern und `removeIf(Predicate filter)` throws `Exception` ist keine gute Sache.

#### Von geprüft nach ungeprüft

Geprüfte Ausnahmen sind in Lamba-Ausdrücken nicht schön. Eine Lösung ist, Code, der geprüfte Ausnahmen auslöst, zu verpacken und die geprüfte Ausnahme in einer ungeprüften zu manteln. Das kann etwa so aussehen:

```java
public class PredicateWithException {

 @FunctionalInterface
 public interface ExceptionalPredicate {
   boolean test( T t ) throws E;
 }

 public static  Predicate asUncheckedPredicate( ExceptionalPredicate predicate ) {
  return t -> {
   try {
    return predicate.test( t );
   }
   catch ( Exception e ) {
    throw new RuntimeException( e.getMessage(), e );
   }
  };
 }
 public static void main( String[] args ) {
  Predicate isEmptyFile = asUncheckedPredicate( path -> Files.size( path ) == 0 );
  System.out.println( isEmptyFile.test( Paths.get( "c:/" ) ) );
 }
}
```

Die Schnittstelle `ExceptionalPredicate` ist ein Prädikat 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ückgibt. Geprüfte Ausnahmen werden in eine ungeprüfte Ausnahme vom Typ `RuntimeException` gesetzt. Somit muss `Predicate` keine geprüfte Ausnahme weiterleiten, was es ja laut Deklaration auch nicht kann.

---

[\[1\]](file:C:/Users/Christian/Dropbox/Eigene%20Dokumente/Insel/todo/1.Lambdas,%20JPB,%20ISP,%20JM,%20zu%20MS.doc#_ftnref1) Von `Callable` gibt es zwar Nutzer, die mit Nebenläufigkeit (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.

Kategorien: [Insel](https://www.tutego.de/blog/javainsel/category/insel/), [Java 8](https://www.tutego.de/blog/javainsel/category/java-8/)

## 3 Kommentare

**Christian Ullenboom** · [17.10.2014 18:24](#comment-57896)

Alles mit throws Exception zu setzen ist eine schlechte Idee, denn so wird dem Aufrufer ja überhaupt nicht klar, WAS genau passieren kann -- Exception ist die Basisklasse aller Ausnahmen (lassen wir Throwable einmal außen vor). Wir wollen aber so präzise Angaben wir möglich, eine IOException ist etwas anderes als eine ArithmeticExcecption.

**Hartmut** · [12.10.2014 16:50](#comment-56668)

Hallo Christian, Danke für Deinen Bericht! Der ist sehr hilfreich. Jedoch kann ich dem Statement "Ausnahmen in Methoden funktionaler Schnittstellen schränken den Nutzen stark ein, und daher löst keine der funktionalen Schnittstellen aus etwa java.util.function eine geprüfte Ausnahme aus. Der Grund ist einfach, denn jeder Methodenaufrufer müsste sonst entweder die Ausnahme weiterleiten oder behandeln" nicht so ganz zustimmen. Es ist doch in Java gar kein Problem, die meisten der selbst erstellten Methoden mit "... throws Exception" abzuschließen - das ist mir viel vertrauter als das Gegenteil. Denn die meisten Programm hängen doch weitestgehend von der "externen" Input/Output-Welt ab, wo jederzeit Exceptions auftreten können (Datenbank, Serververbindungen, etc.). Und je komplexer Software wird, desto mehr wird sie von Funktionen abhängen, die wiederum Exceptions auslösen können. "Leitet der test(….)-Aufrufer mit throws Exception die Ausnahme weiter nach oben, bekommen wir plötzlich an allen Stellen ein throws Exception in die Methodensignatur, was auf keinen Fall gewünscht ist. " Warum soll das nicht gewünscht sein? Ich sehe darin überhaupt keine Nachteile. Schließlich macht es Exception Handling über den ganzen Call-Stack hin transparent und nachvollziehbar. Ich würde viel lieber empfehlen, eigen FunctionalInterfaces zu stricken, die jeweils z.B. eine appy(...) Methode haben, die mit "throws Exception" abschließt. Man kann den Exceptiontype falls nötig ja auch generisch gestalten, für meine Fälle ist dies aber meistens nicht erforderlich. Freue mich natürlich auch über Deine Hinweise, vlt. habe ich einen speziellen Aspekt noch nicht bedacht. Schöne Grüße!

**Eric Giese** · [22.03.2014 17:09](#comment-5703)

Meine Erfahrungen mit Checked Exceptions und Java8 sind inzwischen dass es am besten ist erstere gezielt zu deaktivieren wenn sie die Lambdas stören. Dazu verwende ich in einer Sammlung statischer Methoden "sneakyThrow(ExceptionalConsumer/ExceptionalRunnable/ExceptionalPredicate)" dasselbe Pattern wie du hier beschreibst. Jedoch leite ich die Exception direkt durch anstatt sie in unchecked exceptions umzukapseln. Dazu verwende ich den alten Trick mit Generics: http://james-iry.blogspot.de/2010/08/on-removing-java-checked-exceptions-by.html Außerhalb des Streams/forEach deklariere ich die Exception dann wieder als throws um "sauber" zu bleiben. Das finde ich sehr viel einfacher als die Exception ständig mit "Unchecked(IO/Execution/XML/CloneNotSupported;-))-Exception ein- und wieder auszupacken. Lombok's SneakyThrows ist auch eine Alternative, jedoch nicht in allen Projekten erlaubt. Für die Zukunft hoffe ich das Java selbst Annotationen einführt mit denen sich die Checked Exceptions a la Lombok deaktivieren kann, denn das derzeitige Verhalten und Lambda Expressions passen einfach nicht zusammen. @SupressWarnings und @SafeVarargs machen ja auch nichts anderes als das Typsystem zu beschwichtigen. Ich verstehe deshalb nicht warum man gerade an den Checked Exceptions weiterhin wie an einem Mantra festhält.

Die bisherigen Kommentare wurden aus WordPress übernommen. Neue Kommentare sind derzeit geschlossen.

[← Alle Beiträge](https://www.tutego.de/blog/javainsel/)

---

## Navigation

- Previous: [Die Umgebung der Lambda-Ausdrücke und Variablenzugriffe](https://www.tutego.de/blog/javainsel/2014/03/java-8-umgebung-lambda-ausdruecke-variablenzugriffe.md)
- Next: [Google guice 4.0 beta 4](https://www.tutego.de/blog/javainsel/2014/03/google-guice-4-0-beta-4.md)
