Java 8: Obligatorisch geprüfte Ausnahmebehandlung in Lambda-Ausdrücken. Warum obligatorisch, nicht optional?

70

Ich spiele mit den neuen Lambda-Funktionen in Java 8 und habe festgestellt, dass die von Java 8 angebotenen Methoden wirklich nützlich sind. Ich frage mich jedoch, ob es eine gute Möglichkeit gibt, das folgende Szenario zu umgehen. Angenommen, Sie haben einen Objektpool-Wrapper, für den eine Art Factory erforderlich ist, um den Objektpool zu füllen, z. B. (mit java.lang.functions.Factory):

public class JdbcConnectionPool extends ObjectPool<Connection> {

    public ConnectionPool(int maxConnections, String url) {
        super(new Factory<Connection>() {
            @Override
            public Connection make() {
                try {
                    return DriverManager.getConnection(url);
                } catch ( SQLException ex ) {
                    throw new RuntimeException(ex);
                }
            }
        }, maxConnections);
    }

}

Nach der Umwandlung der Funktionsschnittstelle in einen Lambda-Ausdruck sieht der obige Code folgendermaßen aus:

public class JdbcConnectionPool extends ObjectPool<Connection> {

    public ConnectionPool(int maxConnections, String url) {
        super(() -> {
            try {
                return DriverManager.getConnection(url);
            } catch ( SQLException ex ) {
                throw new RuntimeException(ex);
            }
        }, maxConnections);
    }

}

Nicht so schlimm, aber die aktivierte Ausnahme java.sql.SQLExceptionerfordert einen try/ catchBlock innerhalb des Lambda. In meiner Firma verwenden wir lange Zeit zwei Schnittstellen:

  • IOut<T>das ist ein Äquivalent zu java.lang.functions.Factory;
  • und eine spezielle Schnittstelle für die Fälle, in denen normalerweise die Weitergabe geprüfter Ausnahmen erforderlich ist : interface IUnsafeOut<T, E extends Throwable> { T out() throws E; }.

Beide IOut<T>und IUnsafeOut<T>sollen während der Migration auf Java 8 entfernt werden, es gibt jedoch keine genaue Übereinstimmung für IUnsafeOut<T, E>. Wenn die Lambda-Ausdrücke mit geprüften Ausnahmen umgehen könnten, als wären sie nicht markiert, könnte es möglich sein, im obigen Konstruktor einfach Folgendes zu verwenden:

super(() -> DriverManager.getConnection(url), maxConnections);

Das sieht viel sauberer aus. Ich sehe, dass ich die Superklasse umschreiben kann ObjectPool, um unsere zu akzeptieren IUnsafeOut<T>, aber soweit ich weiß, ist Java 8 noch nicht fertig, daher könnte es einige Änderungen geben wie:

  • etwas ähnliches implementieren wie IUnsafeOut<T, E>? (Um ehrlich zu sein, halte ich das für schmutzig - das Thema muss wählen, was akzeptiert werden soll: entweder Factoryoder "unsichere Fabrik", die keine kompatiblen Methodensignaturen haben kann)
  • einfach geprüfte Ausnahmen in Lambdas ignorieren, also keine Notwendigkeit in IUnsafeOut<T, E>Ersatz? (Warum nicht? zB eine weitere wichtige Änderung: OpenJDK, das ich verwende, javacerfordert jetzt nicht, dass Variablen und Parameter deklariert werden final, um in einer anonymen Klasse [Funktionsschnittstelle] oder einem Lambda-Ausdruck erfasst zu werden.)

Die Frage ist also im Allgemeinen: Gibt es eine Möglichkeit, geprüfte Ausnahmen in Lambdas zu umgehen, oder ist dies in Zukunft geplant, bis Java 8 endgültig veröffentlicht wird?


Update 1

Hm-mm, soweit ich weiß, was wir derzeit haben, scheint es im Moment keinen Weg zu geben, obwohl der Artikel, auf den verwiesen wird, aus dem Jahr 2010 stammt: Brian Goetz erklärt die Ausnahmetransparenz in Java . Wenn sich in Java 8 nicht viel geändert hat, kann dies als Antwort angesehen werden. Auch Brian sagt, dass interface ExceptionalCallable<V, E extends Exception>(was ich IUnsafeOut<T, E extends Throwable>aus unserem Code-Erbe erwähnt habe) so ziemlich nutzlos ist, und ich stimme ihm zu.

Vermisse ich noch etwas anderes?

Lyubomyr Shaydariv
quelle
3
Für alle, die die Entwicklung der Lambdas-API verfolgen, ist es erwähnenswert, dass java.util.functions.Factory :: make jetzt java.util.function.Supplier :: get ist. Sie können eine aktuelle Version der API-Dokumente auf lambdadoc.net sehen , einer Unterwebsite der [Lambda FAQ] ( lambdafaq.org )
Maurice Naftalin
@ MauriceNaftalin, danke für den Kommentar. Ich verwende derzeit OpenJDK 1.8.0-EA.
Lyubomyr Shaydariv
1
Ein Ansatz wird in diesem Blog gegeben
Matt
@ Matt, danke für den Post-Link!
Lyubomyr Shaydariv
3
Die Beschreibung der Ausnahmetransparenz, auf die Sie sich beziehen, war lediglich ein Kandidatenvorschlag, der später nach eingehenderer Prüfung als fehlerhaft eingestuft wurde.
Brian Goetz

Antworten:

45

Sie sind sich nicht sicher, ob ich Ihre Frage wirklich beantworte, aber können Sie so etwas nicht einfach verwenden?

public final class SupplierUtils {
    private SupplierUtils() {
    }

    public static <T> Supplier<T> wrap(Callable<T> callable) {
        return () -> {
            try {
                return callable.call();
            }
            catch (RuntimeException e) {
                throw e;
            }
            catch (Exception e) {
                throw new RuntimeException(e);
            }
        };
    }
}

public class JdbcConnectionPool extends ObjectPool<Connection> {

    public JdbcConnectionPool(int maxConnections, String url) {
        super(SupplierUtils.wrap(() -> DriverManager.getConnection(url)), maxConnections);
    }
}
JB Nizet
quelle
3
Vielen Dank! Ja, Ihr Ansatz scheint eine mögliche Lösung in Bezug auf die aktuellen Java 8-Funktionen zu sein (dies wurde auch von Matt in einem Post-Kommentar über Guava Throwables java8blog.com/post/37385501926/… vorgeschlagen ). Es scheint auch die eleganteste Lösung zu sein, aber ich denke, Sie stimmen auch zu, das ist eine kleine Kesselplatte. Brian Goetz hat 2010 versucht, dieses Problem mithilfe von Parametern vom Typ Variadic zu lösen , und wahrscheinlich wird Java dies irgendwie beheben , wer weiß.
Lyubomyr Shaydariv
10
Ein weiteres gutes Beispiel für den Missbrauch von geprüften Ausnahmen. In diesem Fall kann es erforderlich sein, die Ausnahme in RuntimeException zu verpacken. Wenn Sie sie jedoch nicht erneut nach außen entpacken, wird der gesamte Punkt der überprüften Ausnahmen zunichte gemacht. Bitte nehmen Sie sich die Zeit, um zu lesen, warum geprüfte Ausnahmen existieren und wann sie verwendet werden sollten: stackoverflow.com/a/19061110/14731
Gili
16
@Gili Überprüfte Ausnahmen sind ein Missbrauch des Programmierers, nicht umgekehrt. Sie fördern Boilerplate-Code, zufällige Komplexität und regelrechte Programmierfehler. Sie unterbrechen den Programmfluss, insbesondere weil try-catches sich um eine Aussage und nicht um einen Ausdruck handelt. Das Umschließen von Ausnahmen ist a) ein Ärgernis, b) kaum mehr als ein Hack und umgeht das grundlegende Fehldesign.
Marko Topolnik
7
99% der realen Programmlogik erfordern geprüfte und nicht aktivierte Ausnahmen, um die aktuelle Arbeitseinheit zu durchbrechen und sie an der Ausnahmesperre der obersten Ebene einheitlich zu behandeln. Diese seltenen Fälle, in denen die Ausnahme Teil der regulären Programmlogik ist, werden vom Programmierer nicht übersehen und müssen vom Compiler mitgeteilt werden.
Marko Topolnik
2
@Gili Dein Beispiel ist genau der seltene Fall, den ich erwähne. Auf der Serverseite sind solche Fälle noch seltener. Bei Ihrem Vergleich mit C können Ausnahmen - im Gegensatz zu Fehlercodes - nur explizit mit leeren Catch-Blöcken ignoriert werden. Lustigerweise passiert genau das, wenn Entwickler gezwungen sind, etwas gegen geprüfte Ausnahmen zu unternehmen. Ihre Aussage, dass ich keine Ausnahmen mag, klingt so, als würden Sie versuchen, lustig zu sein, aber es tut mir leid, dass ich den Humor vermisse.
Marko Topolnik
34

In der Lambda-Mailingliste wurde dies ausführlich besprochen . Wie Sie sehen können, schlug Brian Goetz dort vor, dass die Alternative darin besteht, einen eigenen Kombinator zu schreiben:

Oder Sie könnten Ihren eigenen trivialen Kombinator schreiben:

static<T> Supplier<T> exceptionWrappingSupplier(Supplier<T> b) {
     return e -> {
         try { b.accept(e); }
         catch (Exception e) { throw new RuntimeException(e); }
     };
}

Sie können es einmal schreiben, in weniger als der Zeit, die zum Schreiben Ihrer ursprünglichen E-Mail benötigt wurde. Und ähnlich einmal für jede Art von SAM, die Sie verwenden.

Ich würde es vorziehen, wenn wir dies als "Glas 99% voll" betrachten und nicht als Alternative. Nicht alle Probleme erfordern neue Sprachfunktionen als Lösungen. (Ganz zu schweigen davon, dass neue Sprachfunktionen immer neue Probleme verursachen.)

In jenen Tagen hieß die Consumer-Schnittstelle Block.

Ich denke, das entspricht der Antwort von JB Nizet .

Später erklärt Brian , warum dies so gestaltet wurde (der Grund des Problems)

Ja, Sie müssten Ihre eigenen außergewöhnlichen SAMs bereitstellen. Aber dann würde die Lambda-Konvertierung mit ihnen gut funktionieren.

Die EG erörterte zusätzliche Sprach- und Bibliotheksunterstützung für dieses Problem und war am Ende der Ansicht, dass dies ein schlechter Kosten-Nutzen-Kompromiss war.

Bibliotheksbasierte Lösungen verursachen eine zweifache Explosion bei SAM-Typen (außergewöhnlich gegen nicht), die für die primitive Spezialisierung schlecht mit vorhandenen kombinatorischen Explosionen interagieren.

Die verfügbaren sprachbasierten Lösungen waren Verlierer eines Kompromisses zwischen Komplexität und Wert. Obwohl es einige alternative Lösungen gibt, werden wir weiter nachforschen - allerdings eindeutig nicht für 8 und wahrscheinlich auch nicht für 9.

In der Zwischenzeit haben Sie die Werkzeuge, um zu tun, was Sie wollen. Ich verstehe, dass Sie es vorziehen, dass wir Ihnen die letzte Meile zur Verfügung stellen (und zweitens ist Ihre Anfrage wirklich eine kaum verhüllte Anfrage nach "Warum geben Sie nicht einfach bereits geprüfte Ausnahmen auf"), aber ich denke, der aktuelle Status lässt dies zu Sie erledigen Ihre Arbeit.

Edwin Dalorzo
quelle
2
Danke für die Antwort. Ehrlich gesagt hoffte ich, dass Lambda-Ausdrücke prüfungsausnahmefreundlich sind und das Universum der geprüften Ausnahmen an Lambdas vorbeigeht, da es sich lediglich um eine compilergesteuerte Funktion handelt. Aber kein Glück.
Lyubomyr Shaydariv
1
@ LyubomyrShaydariv Ich denke, die Expertengruppe hatte mit mehreren Designproblemen zu kämpfen. Die Notwendigkeit, Anforderung oder Einschränkung, die Abwärtskompatibilität aufrechtzuerhalten, machte die Dinge schwierig. Dann haben wir andere wichtige Probleme wie das Fehlen von Werttypen, das Löschen von Typen und überprüfte Ausnahmen. Wenn Java das erste und das Fehlen der beiden anderen gehabt hätte, wäre das Design von JDK 8 sehr unterschiedlich gewesen. Wir müssen alle verstehen, dass es ein schwieriges Problem mit vielen Kompromissen war und die EG irgendwo eine Grenze ziehen und eine Entscheidung treffen musste. Das mag uns nicht immer gefallen, aber es werden sicherlich Problemumgehungen vorgeschlagen.
Edwin Dalorzo
3
@ LyubomyrShaydariv Eine funktionale Schnittstelle ist wie jede andere Schnittstelle. Sie können es über einen Lambda-Ausdruck oder manuell implementieren. Wenn Sie eine aktivierte Ausnahme innerhalb der Implementierung einer Schnittstellenmethode auslösen würden, die diese Ausnahme nicht deklariert, brechen Sie die Semantik der Programmiersprache. Sie lösen eine Ausnahme aus, die vom Code für Implementierungen der Schnittstelle nicht erwartet wird. Ich sehe nicht, dass das in Ordnung ist. Ich brauche ein ausführlicheres Beispiel für Ihren Vorschlag, damit er für mich Sinn ergibt. Vielleicht sollten Sie dafür eine weitere Diskussion erstellen.
Edwin Dalorzo
2
Nun, das einzige, was ich derzeit sehe, wenn Lambda-Ausdrücke geprüft-ausnahmefreundlich waren, ist: try { iDontHaveThrowsDeclared(x -> someIOOperation(x)) } catch ( IOException ) { ... }wird nicht kompiliert, da IOExceptionim catchBlock bekannt ist, dass es sich um einen geprüften handelt und der iDontHaveThrowsDeclaredkeine throwsKlausel enthält. Also ja, es bricht.
Lyubomyr Shaydariv
1
@EdwinDalorzo, für den Benutzer ist ein Lambda-Ausdruck so, als hätte er keinen Funktionskörper, er läuft vollständig im Kontext, in dem der Lambda-Ausdruck geschrieben ist. Aus dem gleichen Grund ist es natürlich zu erwarten, dass die Ausnahme in demselben Kontext außerhalb des Lambda-Ausdrucks innerhalb der Methode behandelt werden kann. Das Problem ist, dass die eigentliche Ausführung zu einem viel späteren Zeitpunkt in einem völlig anderen Kontext erfolgen kann, möglicherweise als Teil einer API. Wir können nicht erwarten, dass eine API jede denkbare überprüfte Ausnahme behandelt. Das Einschließen einer ungeprüften Ausnahme erscheint angemessen.
YoYo
5

September 2015:

Sie können hierfür ET verwenden . ET ist eine kleine Java 8-Bibliothek für die Konvertierung / Übersetzung von Ausnahmen.

Mit ET können Sie schreiben:

super(() -> et.withReturningTranslation(() -> DriverManager.getConnection(url)), maxConnections);

Mehrzeilige Version:

super(() -> {
  return et.withReturningTranslation(() -> DriverManager.getConnection(url));
}, maxConnections);

Sie müssen lediglich eine neue ExceptionTranslatorInstanz erstellen :

ExceptionTranslator et = ET.newConfiguration().done();

Diese Instanz ist threadsicher und kann von mehreren Komponenten gemeinsam genutzt werden. Sie können spezifischere Ausnahmekonvertierungsregeln (z. B. FooCheckedException -> BarRuntimeException) konfigurieren, wenn Sie möchten. Wenn keine anderen Regeln verfügbar sind, werden aktivierte Ausnahmen automatisch in konvertiert RuntimeException.

(Haftungsausschluss: Ich bin der Autor von ET)

micha
quelle
4

Haben Sie darüber nachgedacht, eine (nicht aktivierte) RuntimeException-Wrapper-Klasse zu verwenden, um die ursprüngliche Ausnahme aus dem Lambda-Ausdruck zu schmuggeln und die umschlossene Ausnahme dann wieder in die ursprünglich aktivierte Ausnahme umzuwandeln ?

class WrappedSqlException extends RuntimeException {
    static final long serialVersionUID = 20130808044800000L;
    public WrappedSqlException(SQLException cause) { super(cause); }
    public SQLException getSqlException() { return (SQLException) getCause(); }
}

public ConnectionPool(int maxConnections, String url) throws SQLException {
    try {
        super(() -> {
            try {
                return DriverManager.getConnection(url);
            } catch ( SQLException ex ) {
                throw new WrappedSqlException(ex);
            }
        }, maxConnections);
    } catch (WrappedSqlException wse) {
        throw wse.getSqlException();
    }
}

Das Erstellen einer eigenen eindeutigen Klasse sollte verhindern, dass eine andere ungeprüfte Ausnahme mit der in Ihrem Lambda verpackten Ausnahme verwechselt wird, selbst wenn die Ausnahme irgendwo in der Pipeline serialisiert wird, bevor Sie sie abfangen und erneut auslösen.

Hmm ... Das einzige, was ich hier sehe, ist, dass Sie dies in einem Konstruktor mit einem Aufruf von super () tun, was laut Gesetz die erste Aussage in Ihrem Konstruktor sein muss. Zählt tryals vorherige Aussage? Ich habe dies (ohne den Konstruktor) in meinem eigenen Code.

GlenPeterson
quelle
Danke für deine Antwort. Ich möchte wirklich verhindern, dass try/catchLambda-Ausdrücke explizit verwendet werden, da try/catchBlöcke hässlich aussehen und die Lambdas auch hässlich machen. Ich denke, es gibt mindestens zwei Gründe, warum es bisher nicht so funktionieren kann: 1) die Geschichte von Java selbst; 2) Eine Methode, die mit einem Lambda-Ausdruck aufgerufen wird, der Methoden mit throwsAufrufen enthält, sollte wahrscheinlich "automatisch" als " throws SomeEx " deklariert werden , selbst wenn die Methode ohne " any " deklariert ist throwsoder eine Ausnahme eines anderen Typs auslöst , anstatt dies zu sein aus dem Lambda vermehrt.
Lyubomyr Shaydariv
3) Wenn es geprüfte Ausnahmen "verschlucken" könnte, denke ich, könnte es Ihre Erwartungen an eine geprüfte Ausnahme oder eine ungeprüfte Ausnahme brechen, die Sie bei einem Anrufer außerhalb erwarten könnten. Zum Beispiel könnten Sie erwarten, IOExceptionnach derzeit illegalem Code wie list.forEach(p -> outputStream.write(p.hashCode()))( write()throw IOException) zu suchen , aber Sie würden es wahrscheinlich nicht tun, da dies Iterable.forEach()nicht als werfende Methode deklariert ist IOException, sodass ein Compiler diese Absichten nicht kennen kann. Manchmal sind geprüfte Ausnahmen wirklich schädlich ...
Lyubomyr Shaydariv
Genau. Es wäre übertrieben, einen zweiten Satz von java.util.function-Klassen bereitzustellen, die deklarieren, dass sie eine (aktivierte) Ausnahme auslösen. Zumal wir zwei Versionen jeder Methode mit einem Funktionsparameter benötigen würden - eine, die auslöst, und eine, die dies nicht tut. Dies erklärt in hohem Maße, warum Scala keine Ausnahmen überprüft hat (nur nicht aktiviert). Java 8 ist eine Kopie von Scalas Lambdas mit einem ->statt =>. Ich frage mich, ob Martin Odersky während seiner Arbeit bei Sun irgendetwas davon vorgeschlagen hat. Ich hoffe, Java 9 enthält unveränderliche Versionen der meisten ihrer API-Klassen.
GlenPeterson
3

Wir haben in meinem Unternehmen ein internes Projekt entwickelt, das uns dabei geholfen hat. Wir haben uns vor zwei Monaten entschlossen, an die Börse zu gehen.

Folgendes haben wir uns ausgedacht:

@FunctionalInterface
public interface ThrowingFunction<T,R,E extends Throwable> {
R apply(T arg) throws E;

/**
 * @param <T> type
 * @param <E> checked exception
 * @return a function that accepts one argument and returns it as a value.
 */
static <T, E extends Exception> ThrowingFunction<T, T, E> identity() {
    return t -> t;
}

/**
 * @return a Function that returns the result of the given function as an Optional instance.
 * In case of a failure, empty Optional is returned
 */
static <T, R, E extends Exception> Function<T, Optional<R>> lifted(ThrowingFunction<T, R, E> f) {
    Objects.requireNonNull(f);

    return f.lift();
}

static <T, R, E extends Exception> Function<T, R> unchecked(ThrowingFunction<T, R, E> f) {
    Objects.requireNonNull(f);

    return f.uncheck();
}

default <V> ThrowingFunction<V, R, E> compose(final ThrowingFunction<? super V, ? extends T, E> before) {
    Objects.requireNonNull(before);

    return (V v) -> apply(before.apply(v));
}

default <V> ThrowingFunction<T, V, E> andThen(final ThrowingFunction<? super R, ? extends V, E> after) {
    Objects.requireNonNull(after);

    return (T t) -> after.apply(apply(t));
}

/**
 * @return a Function that returns the result as an Optional instance. In case of a failure, empty Optional is
 * returned
 */
default Function<T, Optional<R>> lift() {
    return t -> {
        try {
            return Optional.of(apply(t));
        } catch (Throwable e) {
            return Optional.empty();
        }
    };
}

/**
 * @return a new Function instance which wraps thrown checked exception instance into a RuntimeException
 */
default Function<T, R> uncheck() {
    return t -> {
        try {
            return apply(t);
        } catch (final Throwable e) {
            throw new WrappedException(e);
        }
    };
}

}}

https://github.com/TouK/ThrowingFunction/

Grzegorz Piwowarek
quelle
1

Das Umschließen der Ausnahme auf die beschriebene Weise funktioniert nicht. Ich habe es versucht und erhalte immer noch Compilerfehler, was tatsächlich der Spezifikation entspricht: Der Lambda-Ausdruck löst die Ausnahme aus, die mit dem Zieltyp des Methodenarguments nicht kompatibel ist: Callable; call () wirft es nicht, so dass ich den Lambda-Ausdruck nicht als Callable übergeben kann.

Im Grunde gibt es also keine Lösung: Wir sind beim Schreiben von Boilerplate festgefahren. Das einzige, was wir tun können, ist unsere Meinung zu äußern, dass dies behoben werden muss. Ich denke, die Spezifikation sollte einen Zieltyp, der auf inkompatiblen ausgelösten Ausnahmen basiert, nicht einfach blind verwerfen, sondern anschließend prüfen, ob die ausgelöste inkompatible Ausnahme im aufrufenden Bereich abgefangen oder als Auslöser deklariert wird. Und für Lambda-Ausdrücke, die nicht inline sind, schlage ich vor, dass wir sie als stillschweigend ausgelöste geprüfte Ausnahme markieren können (still in dem Sinne, dass der Compiler nicht prüfen sollte, aber die Laufzeit immer noch abfangen sollte). Markieren wir diese mit => im Gegensatz zu -> Ich weiß, dass dies keine Diskussionsseite ist, aber da dies die einzige Lösung für die Frage ist, lassen Sie sich hören und ändern Sie diese Spezifikation!

Waldo auf der Springe
quelle
Die Verpackung wird beschrieben wie es ein aufgegebenes ausnahme weniger imitieren könnte Supplier<T>statt Callable<T>- , die ein Surrogat Weg ist und es funktioniert für die meisten Fälle , dass ich konfrontiert mit. Ich persönlich würde das Verhalten von Lambda-Ausdrücken / Funktionsschnittstellen für geprüfte Ausnahmen ändern. Ich habe jedoch Zweifel, dass das Lambda-Dev-Team zustimmen wird, dieses Verhalten für Lambda zu ändern. Leider sind von Java geprüfte Ausnahmen absichtlich gut, in der Praxis jedoch schädlich. Dies ist ähnlich eine geprüfte Ausnahme in Einwickeln RuntimeExceptioninnerhalb try/catchin jedem Verfahren.
Lyubomyr Shaydariv
Nun, es gibt eine Möglichkeit, die aktivierte Ausnahme generisch zu verpacken, die etwas eloborierter ist als beschrieben, siehe meine bevorstehende Antwort. Trotzdem stimme ich Ihnen zu, dass unser Programmierleben besser wäre, wenn der Compiler etwas härter arbeiten und es uns ermöglichen würde, geprüfte Ausnahmen in unsere Lambdas zu werfen. Derzeit ist es uns nicht möglich, bestimmte geprüfte Ausnahmen abzufangen, die dort auftreten, was uns zu einer hässlichen, umständlichen Codierung zwingt. igitt!
Waldo auf der Springe
Übrigens können Sie versuchen, dem Lambda-Dev-Team über die Mailingliste Ihre Idee für eine vereinfachte Behandlung geprüfter Ausnahmen vorzuschlagen. Wenn sie Ihre E-Mail genehmigen (nicht die Idee selbst), kennen Sie möglicherweise ihre Vor- und Nachteile für die Änderung der Spezifikation und wahrscheinlich auch persönliche Ideen und Gedanken von Brian Goetz.
Lyubomyr Shaydariv
Danke für den Tipp. Ich habe das tatsächlich versucht, aber der Vorschlag wurde völlig ignoriert. Die Reaktionen der Beiträge zu geprüften Ausnahmen sehen nicht allzu positiv aus. Trotzdem hoffe ich, dass die Leute, die diese Bedenken teilen, sich äußern, solange noch etwas Zeit bleibt, um sie zu reparieren. Das Java Lambda sieht so gut aus, dass dies eine Korrektur verdient.
Waldo auf der Springe
1

Paguro bietet funktionale Schnittstellen, die geprüfte Ausnahmen umschließen . Ich habe ein paar Monate, nachdem Sie Ihre Frage gestellt haben, angefangen, daran zu arbeiten, also waren Sie wahrscheinlich Teil der Inspiration dafür!

Sie werden feststellen, dass es in Paguro nur 4 funktionale Schnittstellen gibt, im Vergleich zu den 43 in Java 8 enthaltenen Schnittstellen. Dies liegt daran, dass Paguro Generika gegenüber Grundelementen bevorzugt.

Paguro hat Single-Pass-Transformationen in seine unveränderlichen Sammlungen eingebaut (kopiert von Clojure). Diese Transformationen entsprechen in etwa Clojure-Wandlern oder Java 8-Streams, akzeptieren jedoch funktionale Schnittstellen, die geprüfte Ausnahmen umschließen. Siehe: die Unterschiede zwischen Paguro und Java 8 Streams .

GlenPeterson
quelle
Das ist cooles Zeug und es ist schön, ein Teil Ihrer Inspiration zu sein. :) Drei Jahre nach dem Post kann ich die außergewöhnlichen Zwillinge der Standard-Funktionsschnittstellen nicht vollständig ablehnen. Ich bin mir ziemlich sicher, dass es nur daran liegt, dass ich zu faul bin, um eine weitere Geschäftslogikschnittstelle zu erstellen. Normalerweise kombiniere ich sie nie mit der Verarbeitung von Streams, und ich denke, dass geprüfte Ausnahmen eine großartige Option sein können, insbesondere für die schichtübergreifende Kommunikation.
Lyubomyr Shaydariv
1
Ich denke jetzt, in diesen Jahren, dass meine anfängliche Frage aus der Sicht des Designs falsch war: Warum wollte ich, dass eine SupplierInstanz überhaupt eine Fabrik ist? Jetzt denke ich, dass dieser schmutzige Konstruktor in der Post nur so etwas wie einen delegieren sollte IConnectionFactory, der deklariert SQLException, dass er geworfen werden soll, um die Absichten einer solchen Schnittstelle klar zu enthüllen, die in Zukunft mehr oder weniger leicht erweitert werden könnte.
Lyubomyr Shaydariv
1

Sie können von Ihren Lambdas werfen, sie müssen nur "auf Ihre eigene Weise" deklariert werden (was sie leider nicht in Standard-JDK-Code wiederverwendbar macht, aber hey, wir tun, was wir können).

@FunctionalInterface
public interface SupplierIOException {
   MyClass get() throws IOException;
}

Oder die allgemeinere Version:

public interface ThrowingSupplier<T, E extends Exception> {
  T get() throws E;
}

Ref hier . Es wird auch erwähnt, dass "sneakyThrow" verwendet wird, um die aktivierten Ausnahmen nicht zu deklarieren, sondern sie dennoch zu werfen. Es tut mir ein bisschen weh, vielleicht eine Option.

Rogerdpack
quelle