647 Stimmen

Java 8 Lambda-Funktion, die eine Ausnahme wirft?

Ich weiß, wie man eine Referenz auf eine Methode erstellt, die einen String-Parameter hat und einen int zurückgibt, es ist:

Function

Allerdings funktioniert das nicht, wenn die Funktion eine Ausnahme wirft, sagen wir, sie ist definiert als:

Integer myMethod(String s) throws IOException

Wie würde ich diese Referenz definieren?

12voto

myui Punkte 236

Sneaky throw idiom ermöglicht das Umgehen von CheckedException im Lambda-Ausdruck. Das Einwickeln einer CheckedException in eine RuntimeException ist nicht gut für eine strikte Fehlerbehandlung.

Es kann als eine Consumer-Funktion in einer Java-Sammlung verwendet werden.

Hier ist eine einfache und verbesserte Version von jib's Antwort.

import static Throwing.rethrow;

@Test
public void testRethrow() {
    thrown.expect(IOException.class);
    thrown.expectMessage("i=3");

    Arrays.asList(1, 2, 3).forEach(rethrow(e -> {
        int i = e.intValue();
        if (i == 3) {
            throw new IOException("i=" + i);
        }
    }));
}

Dies wickelt das Lambda einfach in ein rethrow ein. Es sorgt dafür, dass CheckedException jede Exception neu auswirft, die in Ihrem Lambda geworfen wurde.

public final class Throwing {
    private Throwing() {}

    @Nonnull
    public static  Consumer rethrow(@Nonnull final ThrowingConsumer consumer) {
        return consumer;
    }

    /**
     * Der Compiler sieht die Signatur mit den geworfenen T als RuntimeException-Typ, sodass er die unbehandelte Ausnahme weiterleiten kann.
     * 
     * http://www.baeldung.com/java-sneaky-throws
     */
    @SuppressWarnings("unchecked")
    @Nonnull
    public static  void sneakyThrow(@Nonnull Throwable ex) throws E {
        throw (E) ex;
    }

}

Finden Sie einen vollständigen Code und Unit-Tests hier.

11voto

Daniel Dietrich Punkte 2222

Wenn es Ihnen nichts ausmacht, eine Fremdbibliothek (Vavr) zu verwenden, könnten Sie folgendes schreiben:

CheckedFunction1 f = this::myMethod;

Es hat auch den sogenannten Try-Monad, der Fehler behandelt:

Try(() -> f.apply("test")) // ergibt ein Success(Integer) oder Failure(Throwable)
        .map(i -> ...) // wird nur bei Success ausgeführt
        ...

Bitte lesen Sie mehr hier.

Haftungsausschluss: Ich bin der Schöpfer von Vavr.

8voto

JohnnyO Punkte 517

Sie könnten jedoch Ihr eigenes FunctionalInterface erstellen, das wie folgt wirft..

@FunctionalInterface
public interface UseInstance {
  void accept(T instance) throws X;
}

implementieren Sie es dann mit Lambdas oder Referenzen wie unten gezeigt.

import java.io.FileWriter;
import java.io.IOException;

// Lambda-Ausdrücke und das Execute-Around-Methodenmuster (EAM) zum Verwalten von Ressourcen

public class FileWriterEAM  {
  private final FileWriter writer;

  private FileWriterEAM(final String fileName) throws IOException {
    writer = new FileWriter(fileName);
  }
  private void close() throws IOException {
    System.out.println("Automatisches Schließen aufgerufen...");
    writer.close();
  }
  public void writeStuff(final String message) throws IOException {
    writer.write(message);
  }
  //...

  public static void use(final String fileName, final UseInstance block) throws IOException {

    final FileWriterEAM writerEAM = new FileWriterEAM(fileName);    
    try {
      block.accept(writerEAM);
    } finally {
      writerEAM.close();
    }
  }

  public static void main(final String[] args) throws IOException {

    FileWriterEAM.use("eam.txt", writerEAM -> writerEAM.writeStuff("süß"));

    FileWriterEAM.use("eam2.txt", writerEAM -> {
        writerEAM.writeStuff("wie");
        writerEAM.writeStuff("süß");      
      });

    FileWriterEAM.use("eam3.txt", FileWriterEAM::writeIt);     

  }

 void writeIt() throws IOException{
     this.writeStuff("Wie ");
     this.writeStuff("süß ");
     this.writeStuff("es ist");

 }

}

7voto

SeregaLBN Punkte 209

Sie können den Unthrow-Wrapper verwenden

Function func1 = s -> Unthrow.wrap(() -> myMethod(s));

oder

Function func2 = s1 -> Unthrow.wrap((s2) -> myMethod(s2), s1);

6voto

PaoloC Punkte 3372

Du kannst.

Erweitern von @marcgs UtilException und Hinzufügen von generischem wo nötig: Auf diese Weise zwingt der Compiler dich erneut dazu, Wurfklauseln hinzuzufügen, und alles ist so, als ob du geprüfte Ausnahmen nativ in den Java 8-Streams werfen könntest.

public final class LambdaExceptionUtil {

    @FunctionalInterface
    public interface Function_WithExceptions {
        R apply(T t) throws E;
    }

    /**
     * .map(rethrowFunction(name -> Class.forName(name))) oder .map(rethrowFunction(Class::forName))
     */
    public static  Function rethrowFunction(Function_WithExceptions function) throws E  {
        return t -> {
            try {
                return function.apply(t);
            } catch (Exception exception) {
                throwActualException(exception);
                return null;
            }
        };
    }

    @SuppressWarnings("unchecked")
    private static  void throwActualException(Exception exception) throws E {
        throw (E) exception;
    }

}

public class LambdaExceptionUtilTest {

    @Test
    public void testFunction() throws MyTestException {
        List sizes = Stream.of("ciao", "hello").map(rethrowFunction(s -> transform(s))).collect(toList());
        assertEquals(2, sizes.size());
        assertEquals(4, sizes.get(0).intValue());
        assertEquals(5, sizes.get(1).intValue());
    }

    private Integer transform(String value) throws MyTestException {
        if(value==null) {
            throw new MyTestException();
        }
        return value.length();
    }

    private static class MyTestException extends Exception { }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X