Fungsi Java 8 Lambda yang melempar pengecualian?

470

Saya tahu cara membuat referensi ke metode yang memiliki Stringparameter dan mengembalikan int, itu:

Function<String, Integer>

Namun, ini tidak berfungsi jika fungsi melempar pengecualian, katakan itu didefinisikan sebagai:

Integer myMethod(String s) throws IOException

Bagaimana saya mendefinisikan referensi ini?

Manusia Triton
sumber
1
Terkait: stackoverflow.com/questions/31637892/…
Marko Topolnik
1
... dan yang ini: stackoverflow.com/questions/31270759/…
Vadzim
4
Semua solusi tampak seperti bagaimana, melempar pengecualian Runtime, saya yakin itu bukan solusi yang baik. jadi lebih baik menggunakan java lama untuk loop
Nazeel
5
Bagaimana dengan perpustakaan jool ? cf org.jooq.lambda.Paket tidak diperiksa
chaiyachaiya

Jawaban:

403

Anda harus melakukan salah satu dari yang berikut ini.

  • Jika itu kode Anda, maka tentukan antarmuka fungsional Anda sendiri yang menyatakan pengecualian yang diperiksa:

    @FunctionalInterface
    public interface CheckedFunction<T, R> {
       R apply(T t) throws IOException;
    }

    dan gunakan:

    void foo (CheckedFunction f) { ... }
  • Jika tidak, bungkus Integer myMethod(String s)dengan metode yang tidak menyatakan pengecualian yang diperiksa:

    public Integer myWrappedMethod(String s) {
        try {
            return myMethod(s);
        }
        catch(IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    lalu:

    Function<String, Integer> f = (String t) -> myWrappedMethod(t);

    atau:

    Function<String, Integer> f =
        (String t) -> {
            try {
               return myMethod(t);
            }
            catch(IOException e) {
                throw new UncheckedIOException(e);
            }
        };
jason
sumber
7
Anda benar-benar dapat memperluas Consumeratau Functionjika Anda menggunakan metode default - lihat jawaban saya di bawah ini.
jlb
2
Saya pikir ini bisa dicapai sebagai satu-liner .
Ned Twigg
6
Optimalisasi minor: Alih-alih (String t) -> myWrappedMethod(t), referensi metode this::myWrappedMethodjuga dapat digunakan.
Clashsoft
8
Cara yang lebih umum untuk melakukannya adalah dengan mendefinisikan fungsi yang diperiksa seperti antarmuka publik @FunctionalInterface ini CheckedFunction <T, R, E memperluas Exception> {R apply (Tt) throws E; } Dengan cara itu Anda juga dapat menentukan pengecualian mana yang dilempar fungsinya dan dapat menggunakan kembali antarmuka untuk kode apa pun.
Martin Odhelius
3
Wow. Java lebih buruk daripada yang saya kira
user275801
194

Anda sebenarnya dapat memperluas Consumer(dan Functionlain - lain) dengan antarmuka baru yang menangani pengecualian - menggunakan metode default Java 8 !

Pertimbangkan antarmuka ini (meluas Consumer):

@FunctionalInterface
public interface ThrowingConsumer<T> extends Consumer<T> {

    @Override
    default void accept(final T elem) {
        try {
            acceptThrows(elem);
        } catch (final Exception e) {
            // Implement your own exception handling logic here..
            // For example:
            System.out.println("handling an exception...");
            // Or ...
            throw new RuntimeException(e);
        }
    }

    void acceptThrows(T elem) throws Exception;

}

Kemudian, misalnya, jika Anda memiliki daftar:

final List<String> list = Arrays.asList("A", "B", "C");

Jika Anda ingin mengkonsumsinya (mis. Dengan forEach) dengan beberapa kode yang melempar pengecualian, Anda biasanya telah menyiapkan blok coba / tangkap:

final Consumer<String> consumer = aps -> {
    try {
        // maybe some other code here...
        throw new Exception("asdas");
    } catch (final Exception ex) {
        System.out.println("handling an exception...");
    }
};
list.forEach(consumer);

Tetapi dengan antarmuka baru ini, Anda bisa instantiate dengan ekspresi lambda dan kompiler tidak akan mengeluh:

final ThrowingConsumer<String> throwingConsumer = aps -> {
    // maybe some other code here...
    throw new Exception("asdas");
};
list.forEach(throwingConsumer);

Atau bahkan hanya melemparkannya agar lebih ringkas !:

list.forEach((ThrowingConsumer<String>) aps -> {
    // maybe some other code here...
    throw new Exception("asda");
});

Pembaruan : Sepertinya ada bagian perpustakaan utilitas yang sangat bagus dari Durian yang disebut Kesalahan yang dapat digunakan untuk menyelesaikan masalah ini dengan lebih banyak fleksibilitas. Sebagai contoh, dalam implementasi saya di atas saya telah secara eksplisit mendefinisikan kebijakan penanganan kesalahan ( System.out...atau throw RuntimeException), sedangkan Kesalahan Durian memungkinkan Anda untuk menerapkan kebijakan dengan cepat melalui serangkaian metode utilitas yang besar. Terima kasih telah berbagi , @NedTwigg !.

Penggunaan sampel:

list.forEach(Errors.rethrow().wrap(c -> somethingThatThrows(c)));
jlb
sumber
14
Jadi Anda memiliki serangkaian antarmuka (Fungsi, Konsumen, Pemasok, ...) dan serangkaian kebijakan untuk menangani kesalahan (Melempar, System.out.println, ...). Saya pikir ada cara untuk membuatnya mudah untuk menggunakan kebijakan apa pun dengan segala jenis fungsi, tanpa harus menyalin tempel "ThrowingConsumer, ThrowingFunction, etc.".
Ned Twigg
1
beberapa waktu kemudian ... Saya memutuskan untuk menggunakan pengecualian yang tidak dicentang dan tidak menggunakan antarmuka fungsional tambahan atau perpustakaan baru -> jalan yang mudah, kurang mengetik, pengiriman lebih cepat, bukan.
aliopi
1
Ini adalah versi yang ditingkatkan menggunakan idiom melempar licik. Tidak perlu membuka RuntimeException ke dalam CheckException.
myui
62

Saya pikir kelas DurianErrors menggabungkan banyak pro dari berbagai saran di atas.

Untuk memasukkan Durian dalam proyek Anda, Anda dapat:

Ned Twigg
sumber
Atau Anda bisa menggunakan RxJava karena stream membutuhkan penanganan kesalahan yang melekat dan jika ada sesuatu dalam pipa Anda yang melempar pengecualian ada kemungkinan besar itu mungkin aliran yang dapat diamati. Ini juga tidak memaksa Java 8 pada konsumen hilir perpustakaan.
Adam Gent
2
Harap dicatat bahwa Durian tidak memiliki versi baru sejak Juni 2016. Bukan penghenti acara, tetapi sesuatu yang perlu diingat.
Istvan Devai
9
Pemelihara Durian di sini. Apa yang rusak? Jika pengguna menemukan bug atau fitur penting yang hilang, kami akan merilis perbaikan bug dengan cepat. Pustaka sederhana, sehingga kami belum memiliki laporan bug, jadi kami tidak perlu merilis perbaikan bug apa pun.
Ned Twigg
28

Ini tidak khusus untuk Java 8. Anda mencoba mengkompilasi sesuatu yang setara dengan:

interface I {
    void m();
}
class C implements I {
    public void m() throws Exception {} //can't compile
}
assylias
sumber
15
Pertanyaannya adalah "Bagaimana saya mendefinisikan referensi ini?" . Ini sebenarnya tidak menjawab pertanyaan; itu hanya menjelaskan apa masalahnya.
Dawood ibn Kareem
13

Penafian: Saya belum menggunakan Java 8, hanya membaca tentang itu.

Function<String, Integer>tidak membuang IOException, jadi Anda tidak bisa memasukkan kode apa pun di dalamnya itu throws IOException. Jika Anda memanggil metode yang mengharapkan Function<String, Integer>, maka lambda yang Anda berikan ke metode itu tidak dapat dilempar IOException, titik. Anda dapat menulis lambda seperti ini (saya pikir ini adalah sintaks lambda, tidak yakin):

(String s) -> {
    try {
        return myMethod(s);
    } catch (IOException ex) {
        throw new RuntimeException(ex);
        // (Or do something else with it...)
    }
}

Atau, jika metode yang Anda berikan lambda adalah metode yang Anda tulis sendiri, Anda bisa mendefinisikan antarmuka fungsional baru dan menggunakannya sebagai tipe parameter alih-alih Function<String, Integer>:

public interface FunctionThatThrowsIOException<I, O> {
    O apply(I input) throws IOException;
}
Adam R. Nelson
sumber
tambahkan anotasi @FunctionalInterface sebelum antarmuka Anda, hanya kemudian akan dapat digunakan untuk lambdas.
Gangnus
13
@ Gangnus: @FunctionalInterfaceanotasi tidak diperlukan untuk dapat digunakan untuk lambdas. Disarankan untuk memeriksa kewarasan.
Tanmay Patil
9

Jika Anda tidak keberatan menggunakan lib pihak ke-3 ( VAVR ) Anda bisa menulis

CheckedFunction1<String, Integer> f = this::myMethod;

Ini juga memiliki yang disebut Try monad yang menangani kesalahan:

Try(() -> f.apply("test")) // results in a Success(Integer) or Failure(Throwable)
        .map(i -> ...) // only executed on Success
        ...

Silakan baca lebih lanjut di sini .

Penafian: Saya pencipta Vavr.

Daniel Dietrich
sumber
7

Anda dapat menggunakan pembungkus unthrow

Function<String, Integer> func1 = s -> Unthrow.wrap(() -> myMethod(s));

atau

Function<String, Integer> func2 = s1 -> Unthrow.wrap((s2) -> myMethod(s2), s1);
SeregaLBN
sumber
6

Namun Anda bisa membuat FungsionalInterface Anda sendiri yang melempar seperti di bawah ini ..

@FunctionalInterface
public interface UseInstance<T, X extends Throwable> {
  void accept(T instance) throws X;
}

kemudian implementasikan menggunakan Lambdas atau referensi seperti yang ditunjukkan di bawah ini.

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

//lambda expressions and the execute around method (EAM) pattern to
//manage resources

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("close called automatically...");
    writer.close();
  }
  public void writeStuff(final String message) throws IOException {
    writer.write(message);
  }
  //...

  public static void use(final String fileName, final UseInstance<FileWriterEAM, IOException> 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("sweet"));

    FileWriterEAM.use("eam2.txt", writerEAM -> {
        writerEAM.writeStuff("how");
        writerEAM.writeStuff("sweet");      
      });

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

  }


 void writeIt() throws IOException{
     this.writeStuff("How ");
     this.writeStuff("sweet ");
     this.writeStuff("it is");

 }

}
JohnnyO
sumber
6

Kamu bisa.

Memperluas @marcg UtilExceptiondan menambahkan generik <E extends Exception>jika diperlukan: dengan cara ini, kompiler akan memaksa Anda lagi untuk menambahkan klausa melempar dan semuanya seolah-olah Anda bisa melempar pengecualian yang diperiksa secara native pada stream java 8.

public final class LambdaExceptionUtil {

    @FunctionalInterface
    public interface Function_WithExceptions<T, R, E extends Exception> {
        R apply(T t) throws E;
    }

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

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

}

public class LambdaExceptionUtilTest {

    @Test
    public void testFunction() throws MyTestException {
        List<Integer> sizes = Stream.of("ciao", "hello").<Integer>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 { }
}
PaoloC
sumber
5

Saya punya masalah dengan Class.forName dan Class.newInstance di dalam lambda, jadi saya baru saja melakukannya:

public Object uncheckedNewInstanceForName (String name) {

    try {
        return Class.forName(name).newInstance();
    }
    catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

Di dalam lambda, alih-alih memanggil Class.forName ("myClass"). NewInstance () Saya hanya menelepon uncheckedNewInstanceForName ("myClass")

Sergio
sumber
4

Solusi lain menggunakan pembungkus Fungsi akan mengembalikan salah satu instance dari pembungkus hasil Anda, katakan Sukses, jika semuanya berjalan dengan baik, salah satu contoh dari, katakanlah Kegagalan.

Beberapa kode untuk memperjelas beberapa hal:

public interface ThrowableFunction<A, B> {
    B apply(A a) throws Exception;
}

public abstract class Try<A> {

    public static boolean isSuccess(Try tryy) {
        return tryy instanceof Success;
    }

    public static <A, B> Function<A, Try<B>> tryOf(ThrowableFunction<A, B> function) {
        return a -> {
            try {
                B result = function.apply(a);
                return new Success<B>(result);
            } catch (Exception e) {
                return new Failure<>(e);
            }
        };
    }

    public abstract boolean isSuccess();

    public boolean isError() {
        return !isSuccess();
    }

    public abstract A getResult();

    public abstract Exception getError();
}

public class Success<A> extends Try<A> {

    private final A result;

    public Success(A result) {
        this.result = result;
    }

    @Override
    public boolean isSuccess() {
        return true;
    }

    @Override
    public A getResult() {
        return result;
    }

    @Override
    public Exception getError() {
        return new UnsupportedOperationException();
    }

    @Override
    public boolean equals(Object that) {
        if(!(that instanceof Success)) {
            return false;
        }
        return Objects.equal(result, ((Success) that).getResult());
    }
}

public class Failure<A> extends Try<A> {

    private final Exception exception;

    public Failure(Exception exception) {
        this.exception = exception;
    }

    @Override
    public boolean isSuccess() {
        return false;
    }

    @Override
    public A getResult() {
        throw new UnsupportedOperationException();
    }

    @Override
    public Exception getError() {
        return exception;
    }
}

Kasus penggunaan sederhana:

List<Try<Integer>> result = Lists.newArrayList(1, 2, 3).stream().
    map(Try.<Integer, Integer>tryOf(i -> someMethodThrowingAnException(i))).
    collect(Collectors.toList());
yohan
sumber
4

Masalah ini juga mengganggu saya; ini sebabnya saya membuat proyek ini .

Dengan itu Anda bisa melakukan:

final ThrowingFunction<String, Integer> f = yourMethodReferenceHere;

Ada totla dari 39 interface yang didefinisikan oleh JDK yang memiliki Throwingpadanan seperti itu ; mereka semua @FunctionalInterfacedigunakan dalam stream (basis Streamtetapi juga IntStream, LongStreamdanDoubleStream ).

Dan karena masing-masing dari mereka memperpanjang rekan non-lempar, Anda dapat langsung menggunakannya dalam lambdas juga:

myStringStream.map(f) // <-- works

Perilaku default adalah bahwa ketika Anda melempar lambda melempar pengecualian yang diperiksa, a ThrownByLambdaException dilemparkan dengan pengecualian diperiksa sebagai penyebabnya. Karena itu Anda dapat menangkapnya dan mendapatkan penyebabnya.

Fitur lain juga tersedia.

Fge
sumber
Saya sangat menyukai idenya, saya hanya berharap Anda membuat throwables generic seperti yang disarankan di sini: javaspecialists.eu/archive/Issue221.html , misal: @FunctionalInterface public interface SupplierWithCE<T, X extends Exception> { T get() throws X; }- dengan cara ini pengguna tidak perlu menangkap Throwable, tetapi mengecek pengecualian spesifik sebagai gantinya.
Zoltán
@ Zoltán itu akan merepotkan untuk menyatakan pengecualian masing-masing dan setiap saat; juga, Anda selalu dapat hanya menggunakan, katakanlah, .apply () alih-alih .doApply () dan tangkap ThrownByLambdaException, Anda akan memiliki pengecualian asli sebagai penyebab (atau Anda dapat menggunakan rethrow(...).as(MyRuntimeException.class))
fge
Saya pikir ada (semacam) jalan di sekitar ini .
Ned Twigg
@NedTwigg Saya telah menyelesaikan ini sejak lama juga; Saya sekarang dapat menggunakan Throwing.runnable()dan yang lainnya, selalu dengan kemampuan chaining
fge
Fungsionalitas rantai sangat keren! Komentar saya adalah apakah ThrowingRunnable harus memiliki pengecualian umum atau tidak. Zoltan bertanya apakah perpustakaan Anda dapat memiliki argumen sebagai parameter generik, dan Anda mengatakan itu akan merepotkan untuk digunakan. Tautan saya adalah ke beberapa baris kode yang menunjukkan satu cara untuk membuat pengecualian menjadi generik, tanpa menjadi menyusahkan. Kecuali saya salah baca, pengecualian di perpustakaan Anda tidak umum (yang merupakan pilihan desain yang masuk akal, karena Anda tidak mendapatkan banyak utilitas dengan menjadikannya generik).
Ned Twigg
4

Ada banyak tanggapan hebat yang sudah diposting di sini. Hanya berusaha menyelesaikan masalah dengan perspektif yang berbeda. Hanya 2 sen saya, perbaiki saya jika saya salah di suatu tempat.

Melempar klausa di Fungsional Antarmuka bukanlah ide yang baik

Saya pikir ini mungkin bukan ide yang baik untuk menegakkan melempar IOException karena alasan berikut

  • Bagi saya ini seperti anti-pola untuk Stream / Lambda. Seluruh ide adalah bahwa penelepon akan memutuskan kode apa yang akan diberikan dan bagaimana menangani pengecualian. Dalam banyak skenario, IOException mungkin tidak berlaku untuk klien. Misalnya, jika klien mendapatkan nilai dari cache / memori alih-alih melakukan I / O yang sebenarnya.

  • Selain itu, pengecualian yang menangani dalam aliran menjadi sangat mengerikan. Misalnya, ini kode saya yang akan terlihat jika saya menggunakan API Anda

               acceptMyMethod(s -> {
                    try {
                        Integer i = doSomeOperation(s);
                        return i;
                    } catch (IOException e) {
                        // try catch block because of throws clause
                        // in functional method, even though doSomeOperation
                        // might not be throwing any exception at all.
                        e.printStackTrace();
                    }
                    return null;
                });

    Jelek bukan? Selain itu, seperti yang saya sebutkan di poin pertama saya, bahwa metode doSomeOperation mungkin atau mungkin tidak melempar IOException (tergantung pada implementasi klien / pemanggil), tetapi karena klausa melempar dalam metode FungsionalInterface Anda, saya selalu harus menulis coba tangkap.

Apa yang harus saya lakukan jika saya benar-benar tahu API ini melempar IOException

  • Maka mungkin kita membingungkan FunctionalInterface dengan Antarmuka khas. Jika Anda tahu API ini akan melempar IOException, maka kemungkinan besar Anda juga tahu beberapa perilaku default / abstrak. Saya pikir Anda harus mendefinisikan antarmuka dan menggunakan perpustakaan Anda (dengan implementasi standar / abstrak) sebagai berikut

    public interface MyAmazingAPI {
        Integer myMethod(String s) throws IOException;
    }

    Namun, masalah try-catch masih ada untuk klien. Jika saya menggunakan API Anda dalam aliran, saya masih perlu menangani IOException di blok try-catch yang mengerikan.

  • Berikan API ramah aliran default sebagai berikut

    public interface MyAmazingAPI {
        Integer myMethod(String s) throws IOException;
    
        default Optional<Integer> myMethod(String s, Consumer<? super Exception> exceptionConsumer) {
            try {
                return Optional.ofNullable(this.myMethod(s));
            } catch (Exception e) {
                if (exceptionConsumer != null) {
                    exceptionConsumer.accept(e);
                } else {
                    e.printStackTrace();
                }
            }
    
            return Optional.empty();
        }
    }

    Metode default mengambil objek konsumen sebagai argumen, yang akan bertanggung jawab untuk menangani pengecualian. Sekarang, dari sudut pandang klien, kode akan terlihat seperti ini

    strStream.map(str -> amazingAPIs.myMethod(str, Exception::printStackTrace))
                    .filter(Optional::isPresent)
                    .map(Optional::get).collect(toList());

    Bagus kan? Tentu saja, logger atau logika penanganan lainnya dapat digunakan sebagai pengganti Exception :: printStackTrace.

  • Anda juga dapat mengekspos metode yang mirip dengan https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#exceptionally-java.util.fungsi.Fungsi.Fungsi- . Berarti Anda dapat mengekspos metode lain, yang akan berisi pengecualian dari panggilan metode sebelumnya. Kerugiannya adalah Anda sekarang membuat API Anda stateful, yang berarti bahwa Anda perlu menangani keamanan utas dan yang pada akhirnya akan menjadi hit kinerja. Hanya opsi untuk dipertimbangkan.

TriCore
sumber
Saya setuju bahwa mengubah Exception yang dicentang ke Exception yang tidak dicentang, atau menelan Exception bukanlah ide yang baik karena tidak ada cara untuk mengetahui elemen mana yang Streammengangkat Exception. Jadi, saya menyukai gagasan memiliki penangan pengecualian dan memfilter hasil yang tidak valid. Perhatikan bahwa MyAmazingAPI Anda secara efektif a FunctionalInterface(karena itu Anda dapat menambahkan anotasi @FunctionalInterface). Anda juga bisa memiliki nilai default daripada menggunakan Optional.empty().
Julien Kronegg
4

Idiom throw licik memungkinkan memotong CheckedExceptionekspresi Lambda. Membungkus CheckedExceptionaRuntimeException tidak baik untuk penanganan kesalahan yang ketat.

Ini dapat digunakan sebagai Consumerfungsi yang digunakan dalam koleksi Java.

Berikut adalah versi sederhana dari jawaban jib .

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);
        }
    }));
}

Ini hanya membungkus lambda dalam rethrow . Itu membuat CheckedExceptionrethrow semua Exceptionyang dilemparkan ke lambda Anda.

public final class Throwing {
    private Throwing() {}

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

    /**
     * The compiler sees the signature with the throws T inferred to a RuntimeException type, so it
     * allows the unchecked exception to propagate.
     * 
     * http://www.baeldung.com/java-sneaky-throws
     */
    @SuppressWarnings("unchecked")
    @Nonnull
    public static <E extends Throwable> void sneakyThrow(@Nonnull Throwable ex) throws E {
        throw (E) ex;
    }

}

Temukan kode lengkap dan tes unit di sini .

myui
sumber
3

Anda dapat menggunakan ET untuk ini. ET adalah perpustakaan Java 8 kecil untuk konversi pengecualian / terjemahan.

Dengan ET terlihat seperti ini:

// Do this once
ExceptionTranslator et = ET.newConfiguration().done();

...

// if your method returns something
Function<String, Integer> f = (t) -> et.withReturningTranslation(() -> myMethod(t));

// if your method returns nothing
Consumer<String> c = (t) -> et.withTranslation(() -> myMethod(t));

ExceptionTranslatorinstance aman untuk digunakan bersama oleh banyak komponen. Anda dapat mengonfigurasi aturan konversi pengecualian yang lebih spesifik (mis. FooCheckedException -> BarRuntimeException) Jika Anda mau. Jika tidak ada aturan lain yang tersedia, pengecualian yang dicentang secara otomatis dikonversi ke RuntimeException.

(Penafian: Saya adalah penulis ET)

micha
sumber
2
Sepertinya Anda pembuat perpustakaan ini. Berdasarkan aturan SO , Anda harus mengungkapkan afiliasi Anda dalam jawaban Anda. Silakan tambahkan secara eksplisit ke jawaban Anda bahwa Anda menulis perpustakaan ini (sama dengan jawaban terkait ET lainnya).
Tagir Valeev
2
Hai Tagir, terima kasih atas petunjuknya. Saya memperbarui jawabannya.
micha
2

Yang saya lakukan adalah mengizinkan pengguna untuk memberikan nilai yang sebenarnya ia inginkan jika ada pengecualian. Jadi saya punya sesuatu yang terlihat seperti ini

public static <T, R> Function<? super T, ? extends R> defaultIfThrows(FunctionThatThrows<? super T, ? extends R> delegate, R defaultValue) {
    return x -> {
        try {
            return delegate.apply(x);
        } catch (Throwable throwable) {
            return defaultValue;
        }
    };
}

@FunctionalInterface
public interface FunctionThatThrows<T, R> {
    R apply(T t) throws Throwable;
}

Dan ini bisa disebut seperti:

defaultIfThrows(child -> child.getID(), null)
mmounirou
sumber
1
Ini adalah perpanjangan dari ide ini yang membuat perbedaan antara strategi "nilai default" (seperti dalam jawaban Anda), dan strategi "rethrow RuntimeException", di mana nilai default tidak diperlukan.
Ned Twigg
2

Jika Anda tidak keberatan menggunakan perpustakaan pihak ketiga, dengan cyclops-react , perpustakaan yang saya berkontribusi, Anda dapat menggunakan API FluentFunctions untuk menulis

 Function<String, Integer> standardFn = FluentFunctions.ofChecked(this::myMethod);

ofChecked mengambil jOOλ CheckedFunction dan mengembalikan referensi yang dilunakkan ke standar (tidak dicentang) JDK java.util.function.Function.

Atau Anda dapat tetap bekerja dengan fungsi yang diambil melalui api FluentFunctions!

Misalnya untuk mengeksekusi metode Anda, coba lagi hingga 5 kali dan mencatat status yang bisa Anda tulis

  FluentFunctions.ofChecked(this::myMethod)
                 .log(s->log.debug(s),e->log.error(e,e.getMessage())
                 .try(5,1000)
                 .apply("my param");
John McClean
sumber
2

Secara default, Fungsi Java 8 tidak memungkinkan untuk membuang pengecualian dan seperti yang disarankan dalam beberapa jawaban ada banyak cara untuk mencapainya, salah satu caranya adalah:

@FunctionalInterface
public interface FunctionWithException<T, R, E extends Exception> {
    R apply(T t) throws E;
}

Tentukan sebagai:

private FunctionWithException<String, Integer, IOException> myMethod = (str) -> {
    if ("abc".equals(str)) {
        throw new IOException();
    }
  return 1;
};

Dan tambahkan throwsatau try/catchpengecualian yang sama dalam metode pemanggil.

Arpit Aggarwal
sumber
2

Buat jenis pengembalian kustom yang akan menyebarkan pengecualian yang diperiksa. Ini adalah alternatif untuk membuat antarmuka baru yang mencerminkan antarmuka fungsional yang ada dengan sedikit modifikasi dari "throws exception" pada metode antarmuka fungsional.

Definisi

CheckedValueSupplier

public static interface CheckedValueSupplier<V> {
    public V get () throws Exception;
}

Nilai Checked

public class CheckedValue<V> {
    private final V v;
    private final Optional<Exception> opt;

    public Value (V v) {
        this.v = v;
    }

    public Value (Exception e) {
        this.opt = Optional.of(e);
    }

    public V get () throws Exception {
        if (opt.isPresent()) {
            throw opt.get();
        }
        return v;
    }

    public Optional<Exception> getException () {
        return opt;
    }

    public static <T> CheckedValue<T> returns (T t) {
        return new CheckedValue<T>(t);
    }

    public static <T> CheckedValue<T> rethrows (Exception e) {
        return new CheckedValue<T>(e);
    }

    public static <V> CheckedValue<V> from (CheckedValueSupplier<V> sup) {
        try {
            return CheckedValue.returns(sup.get());
        } catch (Exception e) {
            return Result.rethrows(e);
        }
    }

    public static <V> CheckedValue<V> escalates (CheckedValueSupplier<V> sup) {
        try {
            return CheckedValue.returns(sup.get());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

Pemakaian

//  Don't use this pattern with FileReader, it's meant to be an
//  example.  FileReader is a Closeable resource and as such should
//  be managed in a try-with-resources block or in another safe
//  manner that will make sure it is closed properly.

//  This will not compile as the FileReader constructor throws
//  an IOException.
    Function<String, FileReader> sToFr =
        (fn) -> new FileReader(Paths.get(fn).toFile());

// Alternative, this will compile.
    Function<String, CheckedValue<FileReader>> sToFr = (fn) -> {
        return CheckedValue.from (
            () -> new FileReader(Paths.get("/home/" + f).toFile()));
    };

// Single record usage
    // The call to get() will propagate the checked exception if it exists.
    FileReader readMe = pToFr.apply("/home/README").get();


// List of records usage
    List<String> paths = ...; //a list of paths to files
    Collection<CheckedValue<FileReader>> frs =
        paths.stream().map(pToFr).collect(Collectors.toList());

// Find out if creation of a file reader failed.
    boolean anyErrors = frs.stream()
        .filter(f -> f.getException().isPresent())
        .findAny().isPresent();

Apa yang sedang terjadi?

Antarmuka fungsional tunggal yang melempar pengecualian diperiksa dibuat ( CheckedValueSupplier). Ini akan menjadi satu-satunya antarmuka fungsional yang memungkinkan pengecualian yang diperiksa. Semua antarmuka fungsional lainnya akan memanfaatkan CheckedValueSupplieruntuk membungkus kode apa pun yang melempar pengecualian yang diperiksa.

The CheckedValuekelas akan memegang hasil mengeksekusi logika yang melempar pengecualian diperiksa. Ini mencegah penyebaran pengecualian yang dicentang hingga titik di mana kode mencoba mengakses nilai yang CheckedValueberisi instance .

Masalah dengan pendekatan ini.

  • Kami sekarang melempar "Pengecualian" secara efektif menyembunyikan jenis tertentu yang awalnya dilemparkan.
  • Kami tidak menyadari bahwa pengecualian terjadi hingga CheckedValue#get()dipanggil.

Konsumen et al

Beberapa antarmuka fungsional (Consumer misalnya) harus ditangani dengan cara yang berbeda karena tidak memberikan nilai balik.

Berfungsi sebagai pengganti Konsumen

Salah satu pendekatan adalah menggunakan fungsi alih-alih konsumen, yang berlaku saat menangani stream.

    List<String> lst = Lists.newArrayList();
// won't compile
lst.stream().forEach(e -> throwyMethod(e));
// compiles
lst.stream()
    .map(e -> CheckedValueSupplier.from(
        () -> {throwyMethod(e); return e;}))
    .filter(v -> v.getException().isPresent()); //this example may not actually run due to lazy stream behavior

Meningkatkan

Atau, Anda selalu dapat naik ke RuntimeException. Ada jawaban lain yang mencakup eskalasi pengecualian yang diperiksa dari dalam aConsumer .

Jangan dikonsumsi.

Cukup hindari antarmuka fungsional secara bersamaan dan gunakan loop yang bagus.

justin.hughey
sumber
2

Saya menggunakan fungsi utilitas kelebihan beban yang disebut unchecked()yang menangani beberapa kasus penggunaan.


BEBERAPA PENGGUNAAN EAMPLE

unchecked(() -> new File("hello.txt").createNewFile());

boolean fileWasCreated = unchecked(() -> new File("hello.txt").createNewFile());

myFiles.forEach(unchecked(file -> new File(file.path).createNewFile()));

UTILITAS PENDUKUNG

public class UncheckedUtils {

    @FunctionalInterface
    public interface ThrowingConsumer<T> {
        void accept(T t) throws Exception;
    }

    @FunctionalInterface
    public interface ThrowingSupplier<T> {
        T get() throws Exception;
    }

    @FunctionalInterface
    public interface ThrowingRunnable {
        void run() throws Exception;
    }

    public static <T> Consumer<T> unchecked(
            ThrowingConsumer<T> throwingConsumer
    ) {
        return i -> {
            try {
                throwingConsumer.accept(i);
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        };
    }

    public static <T> T unchecked(
            ThrowingSupplier<T> throwingSupplier
    ) {
        try {
            return throwingSupplier.get();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    public static void unchecked(
            ThrowingRunnable throwing
    ) {
        try {
            throwing.run();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}
Stephen Paul
sumber
0

Beberapa solusi yang ditawarkan menggunakan argumen generik E untuk meneruskan jenis pengecualian yang dilemparkan.

Ambil satu langkah lebih jauh, dan alih-alih meneruskan dalam jenis pengecualian, masukkan Konsumen jenis pengecualian, seperti dalam ...

Consumer<E extends Exception>

Anda dapat membuat beberapa variasi yang dapat digunakan kembali Consumer<Exception>yang akan mencakup kebutuhan penanganan perkecualian umum aplikasi Anda.

Rodney P. Barbati
sumber
0

Saya akan melakukan sesuatu yang umum:

public interface Lambda {

    @FunctionalInterface
    public interface CheckedFunction<T> {

        T get() throws Exception;
    }

    public static <T> T handle(CheckedFunction<T> supplier) {
        try {
            return supplier.get();
        } catch (Exception exception) {
            throw new RuntimeException(exception);

        }
    }
}

pemakaian:

 Lambda.handle(() -> method());
ahll
sumber
0

Gunakan Jool Libraryatau katakan jOOλ librarydariJOOQ . Ini tidak hanya menyediakan pengecualian yang ditangani antarmuka tetapi juga menyediakan kelas Seq dengan metode yang berguna.

Juga, ini berisi Antarmuka Fungsional dengan hingga 16 parameter. Selain itu, ia menyediakan kelas Tuple yang digunakan dalam skenario yang berbeda.

Jool Git Link

Khususnya di pencarian perpustakaan untuk org.jooq.lambda.fi.util.functionpaket. Ini berisi semua antarmuka dari Java-8 dengan diperiksa diperiksa. Lihat di bawah untuk referensi: -

masukkan deskripsi gambar di sini

Vinay Prajapati
sumber
0

Saya penulis lib kecil dengan sihir generik untuk membuang Java Exception di mana saja tanpa perlu menangkapnya atau membungkusnya denganRuntimeException .

Pemakaian: unchecked(() -> methodThrowingCheckedException())

public class UncheckedExceptions {

    /**
     * throws {@code exception} as unchecked exception, without wrapping exception.
     *
     * @return will never return anything, return type is set to {@code exception} only to be able to write <code>throw unchecked(exception)</code>
     * @throws T {@code exception} as unchecked exception
     */
    @SuppressWarnings("unchecked")
    public static <T extends Throwable> T unchecked(Exception exception) throws T {
        throw (T) exception;
    }


    @FunctionalInterface
    public interface UncheckedFunction<R> {
        R call() throws Exception;
    }

    /**
     * Executes given function,
     * catches and rethrows checked exceptions as unchecked exceptions, without wrapping exception.
     *
     * @return result of function
     * @see #unchecked(Exception)
     */
    public static <R> R unchecked(UncheckedFunction<R> function) {
        try {
            return function.call();
        } catch (Exception e) {
            throw unchecked(e);
        }
    }


    @FunctionalInterface
    public interface UncheckedMethod {
        void call() throws Exception;
    }

    /**
     * Executes given method,
     * catches and rethrows checked exceptions as unchecked exceptions, without wrapping exception.
     *
     * @see #unchecked(Exception)
     */
    public static void unchecked(UncheckedMethod method) {
        try {
            method.call();
        } catch (Exception e) {
            throw unchecked(e);
        }
    }
}

sumber: https://github.com/qoomon/unchecked-exceptions-java

qoomon
sumber
-7
public void frankTest() {
    int pageId= -1;

    List<Book> users= null;
    try {
        //Does Not Compile:  Object page=DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> new Portal(rw.getInt("id"), "", users.parallelStream().filter(uu -> uu.getVbid() == rw.getString("user_id")).findFirst().get(), rw.getString("name")));

        //Compiles:
        Object page= DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> { 
            try {
                final Book bk= users.stream().filter(bp -> { 
                    String name= null;
                    try {
                        name = rw.getString("name");
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    return bp.getTitle().equals(name); 
                }).limit(1).collect(Collectors.toList()).get(0);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return new Portal(rw.getInt("id"), "", users.get(0), rw.getString("name")); 
        } );
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
Franky Knuckels
sumber
3
Pikiran untuk mengomentari pekerjaan Anda? Jawaban hanya kode tidak begitu berguna.
Phantômaxx
@ Franky, Anda dapat memperbaiki presentasi Anda dengan menggunakan 4 spasi, bukan <code>/<code>:)
AdrieanKhisbe