Java 8: Akışlarda istisna atma yöntemleriyle nasıl çalışırım?


172

Bir sınıfım ve bir yöntemim olduğunu varsayalım

class A {
  void foo() throws Exception() {
    ...
  }
}

Şimdi aşağıdaki gibi Abir akış tarafından teslim her örneği için foo aramak istiyorum:

void bar() throws Exception {
  Stream<A> as = ...
  as.forEach(a -> a.foo());
}

Soru: İstisnayı nasıl düzgün bir şekilde ele alabilirim? Foo () tarafından atılabilecek olası istisnaları işlemediğim için kod makinemde derlenmiyor. throws ExceptionArasında barburada yararsız gibi görünüyor. Neden?



Yanıtlar:


141

Yöntem çağrınızı, kontrol edilen istisnaları atmadığınız başka bir tanesine sarmanız gerekir . Hala alt sınıfı olan her şeyi atabilirsiniz RuntimeException.

Normal bir sarma deyimi şuna benzer:

private void safeFoo(final A a) {
    try {
        a.foo();
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}

(Süpertip istisna Exceptionedilir sadece kendiniz yakalamaya çalışın asla, örnek olarak kullanılır)

Ardından: ile arayabilirsiniz as.forEach(this::safeFoo).


1
Eğer bir sarıcı yöntem olmasını istiyorsanız bunu statik ilan ederim. 'Bundan' herhangi bir şey kullanmaz.
aalku

208
Yerel istisnalarımızı kullanmak yerine bunu yapmak üzücü .... oh Java, verir ve sonra alır
Erich

1
@Stephan Bu cevap silindi, ancak hala burada mevcut: stackoverflow.com/a/27661562/309308
Michael Mrozek

3
Bu, şirketimdeki kod incelemesinde hemen başarısız olur: denetlenmeyen istisnalar atmamıza izin verilmez.
Stelios Adamantidis

7
@SteliosAdamantidis bu kural inanılmaz derecede kısıtlayıcı ve karşı üretken. tüm api'lerde tüm yöntemlerin, bilmek zorunda kalmadan çalışma zamanı istisnalarının tüm tatlarını atmaya yetkili olduğunu biliyor musunuz (elbette öylesiniz). javascript'i kontrol edilen istisnalar konseptinin uygulanmaması nedeniyle yasakladınız mı? Baş geliştiriciniz olsaydım, bunun yerine kontrol edilen istisnaları yasaklardım.
SPI

35

İstediğiniz tek şey çağırmaksa foove istisnayı olduğu gibi (sarma olmadan) yaymayı tercih ediyorsanız, forbunun yerine sadece Java döngüsünü de kullanabilirsiniz (Akışı bir hile ile Yinelenebilir hale getirdikten sonra ):

for (A a : (Iterable<A>) as::iterator) {
   a.foo();
}

Bu, en azından, JUnit testlerimde yaptığım şeydir, burada kontrol edilen istisnalarımı sarmakla uğraşmak istemiyorum (ve aslında testlerimi açılmamış orijinal olanları atmak için tercih ediyorum)


16

Bu soru biraz eski olabilir, ama burada "doğru" cevabını düşündüğümden, kodunuzun ilerleyen bölümlerinde gizli Sorunlara yol açabilecek sadece bir yol var. Küçük bir Tartışma olsa bile , Kontrol Edilen İstisnalar bir nedenden dolayı vardır.

Bence bulabileceğiniz en zarif yolu Misha tarafından burada verildi Sadece "futures" eylemleri gerçekleştirerek Java 8 akışlarında toplam çalışma zamanı istisnaları . Böylece tüm çalışma parçalarını çalıştırabilir ve çalışmayan İstisnaları tek olarak toplayabilirsiniz. Aksi takdirde, hepsini bir Listede toplayıp daha sonra işleyebilirsiniz.

Benzer bir yaklaşım Benji Weber'den geliyor . Çalışma parçalarını değil çalışma parçalarını toplamak için kendi türünü oluşturmayı önerir.

Gerçekte ne elde etmek istediğinize bağlı olarak, giriş değerleri ve Çıktı Değerleri arasında basit bir eşleme oluştu İstisnalar sizin için de işe yarayabilir.

Bu yollardan herhangi birini beğenmediyseniz (Orijinal İstisnaya bağlı olarak) en azından kendi istisnayı kullanmayı düşünün.


3
Bu doğru bir cevap olarak işaretlenmelidir. +. Ama bunun iyi bir gönderi olduğunu söyleyemem. Bunu büyük ölçüde detaylandırmalısınız. Kendi istisnası nasıl yardımcı olabilir? Meydana gelen istisnalar nelerdir? Neden farklı varyantları buraya getirmediniz? Referanslarda tüm örnek kodlara sahip olmak, SO'da burada yasaklanmış bir yazı stili olarak kabul edilir. Metniniz daha çok yorum gibi görünüyor.
Gangnus

2
Onları hangi düzeyde yakalamak istediğinizi seçebilmelisiniz ve akarsu bununla karışır. Akış API'sı, son işleme (toplama gibi) kadar istisnayı taşımanıza ve orada bir işleyici ile ele alınmasına veya başka şekilde atılmasına izin vermelidir. stream.map(Streams.passException(x->mightThrowException(x))).catch(e->whatToDo(e)).collect(...). İstisnalar beklemeli ve vadeli işlemler gibi bunları ele almanıza izin vermelidir.
aalku

10

Google Guava Throwables sınıfını kullanmanızı öneririm

Propagate ( Atılabil throwable)

RuntimeException veya Error örneğiyse ya da son çare olarak atılabilir gibi yayılır, bunu bir RuntimeException öğesine sarar ve sonra yayılır. **

void bar() {
    Stream<A> as = ...
    as.forEach(a -> {
        try {
            a.foo()
        } catch(Exception e) {
            throw Throwables.propagate(e);
        }
    });
}

GÜNCELLEME:

Artık kullanımdan kaldırıldığına göre:

void bar() {
    Stream<A> as = ...
    as.forEach(a -> {
        try {
            a.foo()
        } catch(Exception e) {
            Throwables.throwIfUnchecked(e);
            throw new RuntimeException(e);
        }
    });
}

4
Bu yöntem kullanımdan kaldırıldı (maalesef).
Robert Važan

9

İstisnaları bu şekilde sarabilir ve açabilirsiniz.

class A {
    void foo() throws Exception {
        throw new Exception();
    }
};

interface Task {
    void run() throws Exception;
}

static class TaskException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    public TaskException(Exception e) {
        super(e);
    }
}

void bar() throws Exception {
      Stream<A> as = Stream.generate(()->new A());
      try {
        as.forEach(a -> wrapException(() -> a.foo())); // or a::foo instead of () -> a.foo()
    } catch (TaskException e) {
        throw (Exception)e.getCause();
    }
}

static void wrapException(Task task) {
    try {
        task.run();
    } catch (Exception e) {
        throw new TaskException(e);
    }
}

9

Aşağıdakilerden birini yapmak isteyebilirsiniz:

  • kontrol edilen istisnayı yay,
  • sarın ve kontrol edilmeyen istisnayı yayın veya
  • istisnayı yakalamak ve yayılmayı durdurmak.

Birkaç kütüphane bunu kolayca yapmanızı sağlar. Aşağıdaki örnek NoException kütüphanem kullanılarak yazılmıştır .

// Propagate checked exception
as.forEach(Exceptions.sneak().consumer(A::foo));

// Wrap and propagate unchecked exception
as.forEach(Exceptions.wrap().consumer(A::foo));
as.forEach(Exceptions.wrap(MyUncheckedException::new).consumer(A::foo));

// Catch the exception and stop propagation (using logging handler for example)
as.forEach(Exceptions.log().consumer(Exceptions.sneak().consumer(A::foo)));

Kütüphanede iyi iş çıkardın! Benzer bir şey yazmak üzereydim.
Jasper de Vries

2

Daha okunabilir yolu:

class A {
  void foo() throws MyException() {
    ...
  }
}

Sadece RuntimeExceptiongeçmişini almak içinforEach()

  void bar() throws MyException {
      Stream<A> as = ...
      try {
          as.forEach(a -> {
              try {
                  a.foo();
              } catch(MyException e) {
                  throw new RuntimeException(e);
              }
          });
      } catch(RuntimeException e) {
          throw (MyException) e.getCause();
      }
  }

Her ne kadar bu noktada akışları atla ve for döngüsü ile devam ederse derken birine karşı durmayacağım, ancak:

  • akışınızı kullanarak Collection.stream()bir for döngüsüne doğrudan ileriye doğru çeviri yapmıyorsunuz.
  • kullanmaya çalışıyorsun parallelstream()
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.