Java 8: Lambda-Akışları, İstisnai Olarak Yöntemle Filtreleme


168

Java 8 Lambda ifadelerini denemekte bir sorun var. Genellikle iyi çalışıyor, ama şimdi IOException's atmak yöntemleri var . Aşağıdaki koda bakarsanız en iyisi:

class Bank{
    ....
    public Set<String> getActiveAccountNumbers() throws IOException {
        Stream<Account> s =  accounts.values().stream();
        s = s.filter(a -> a.isActive());
        Stream<String> ss = s.map(a -> a.getNumber());
        return ss.collect(Collectors.toSet());
    }
    ....
}

interface Account{
    ....
    boolean isActive() throws IOException;
    String getNumber() throws IOException;
    ....
}

Sorun, derlemiyor, çünkü isActive- ve getNumber-Methods olası istisnalarını yakalamak zorundayım. Ancak aşağıdaki gibi açıkça bir try-catch-Block kullansam bile, hala derlenmiyor çünkü İstisna'yı yakalamıyorum. Yani JDK'da bir hata var ya da bu İstisnaları nasıl yakalayacağımı bilmiyorum.

class Bank{
    ....
    //Doesn't compile either
    public Set<String> getActiveAccountNumbers() throws IOException {
        try{
            Stream<Account> s =  accounts.values().stream();
            s = s.filter(a -> a.isActive());
            Stream<String> ss = s.map(a -> a.getNumber());
            return ss.collect(Collectors.toSet());
        }catch(IOException ex){
        }
    }
    ....
}

Nasıl çalıştırabilirim? Birisi beni doğru çözüme yönlendirebilir mi?




4
Basit ve doğru cevap: lambda içindeki istisnayı yakalayın.
Brian Goetz

Yanıtlar:


211

Lambda'dan kaçmadan önce istisnayı yakalamanız gerekir :

s = s.filter(a -> { try { return a.isActive(); } 
                    catch (IOException e) { throw new UncheckedIOException(e); }}});

Lambda'nın yazdığınız yerde değil, JDK sınıfı içinde tamamen ilgisiz bir yerde değerlendirildiğini düşünün. Bu, kontrol edilen istisnanın atılacağı nokta olacaktır ve o yerde beyan edilmemiştir.

Kontrol edilmiş istisnaları kontrol edilmemiş olanlara çeviren bir lambda paketini kullanarak başa çıkabilirsiniz:

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

Örneğiniz şu şekilde yazılacaktır:

return s.filter(a -> uncheckCall(a::isActive))
        .map(Account::getNumber)
        .collect(toSet());

Projelerimde bu konuyu sarmadan hallederim; bunun yerine derleyicinin istisnaları denetlemesini etkili bir şekilde etkisiz hale getiren bir yöntem kullanıyorum. Söylemeye gerek yok, bu dikkatle ele alınmalı ve projedeki herkes, bildirilmediği yerde kontrol edilmiş bir istisnanın ortaya çıkabileceğinin farkında olmalıdır. Sıhhi tesisat kodu:

public static <T> T uncheckCall(Callable<T> callable) {
  try { return callable.call(); }
  catch (Exception e) { return sneakyThrow(e); }
}
public static void uncheckRun(RunnableExc r) {
  try { r.run(); } catch (Exception e) { sneakyThrow(e); }
}
public interface RunnableExc { void run() throws Exception; }


@SuppressWarnings("unchecked")
private static <T extends Throwable> void sneakyThrow(Throwable t) throws T {
  throw (T) t;
}

ve beyan IOExceptionetmese collectde , yüzünüze atılmayı bekleyebilirsiniz . Gelen en hepsi olmasa da gerçek yaşam durumlarda sadece zaten, istisna rethrow ve genel başarısızlık olarak ele almak isterim. Tüm bu durumlarda, netlik veya doğruluk açısından hiçbir şey kaybolmaz. Sadece oradaki istisnaya tepki vermek istediğiniz diğer durumlara dikkat edin. Geliştirici derleyici tarafından IOExceptionorada yakalanması gereken bir şey olduğu konusunda bilgilendirilmeyecek ve derleyici aslında onu yakalamaya çalışırsanız şikayet edecek çünkü bu tür bir istisnanın atılamayacağına inanarak kandırdık.


4
NettyIO'nun daha önce "sinsi atış" yaptığını gördüm ve sandalyemi pencereden dışarı atmak istedim. “Ne? Bu kontrol edilen istisna nereden sızdı?” Bu, henüz gördüğüm sinsi atışlar için ilk yasal kullanım durumu. Bir programcı olarak sinsi atışların mümkün olduğunu gösterme konusunda dikkatli olmalısınız. Belki de daha iyi, sadece kontrol edilen istisnayı destekleyen başka bir akış arayüzü / impl oluşturmak mı?
kevinarpe

8
Hiçbir aklı başında API istemciye bildirilmemiş kontrol edilmiş istisnalar sunmamalıdır, bu kesin. API içinde , kontrol edilen istisnanın sızabileceği konusunda bir anlayış olabilir. Genel başarısızlığın bir başka işareti olduğu ve toptan yakalanıp ele alındığı sürece zarar vermezler.
Marko Topolnik

5
@kevinarpe Sinsi atışların kötü bir fikir olmasının kesin nedeni budur. Derleyicinin kısa devre yapması gelecekteki bakıcıları karıştırmak zorundadır.
Thorbjørn Ravn Andersen

29
Kuralları sevmemeniz, yasayı kendi ellerinize almanın iyi bir fikir olduğu anlamına gelmez. Tavsiyeniz sorumsuzdur, çünkü kod yazarının rahatlığını programın şeffaflığı ve sürdürülebilirliği konusunda çok daha önemli hususlara yerleştirir.
Brian Goetz

34
@brian Bir şeyin kural olması iyi bir fikir olduğu anlamına gelmez. Ama cevabımın ikinci kısmına "tavsiye" olarak bahsettiğinize şaşırdım, çünkü bir çözüm olarak önerdiğim şeyi ve ilgili okuyucuya FYI olarak sunduğum şeyi bolca reddeden açıkça açıkça düşündüğümü düşündüm.
Marko Topolnik

29

Statik ağrınızı lambdalarla da çoğaltabilirsiniz, böylece her şey okunabilir görünüyor:

s.filter(a -> propagate(a::isActive))

propagateburada java.util.concurrent.Callableparametre olarak alınır ve çağrı sırasında yakalanan istisnaları dönüştürür RuntimeException. Benzer bir dönüştürme yöntemi var Guava'da Throwables # propagate (Throwable) .

Bu yöntem lambda yöntemi zincirleme için gerekli gibi görünüyor, bu yüzden umarım bir gün popüler kütüphanelerden birine eklenecek veya bu yayılma davranışı varsayılan olarak olacaktır.

public class PropagateExceptionsSample {
    // a simplified version of Throwables#propagate
    public static RuntimeException runtime(Throwable e) {
        if (e instanceof RuntimeException) {
            return (RuntimeException)e;
        }

        return new RuntimeException(e);
    }

    // this is a new one, n/a in public libs
    // Callable just suits as a functional interface in JDK throwing Exception 
    public static <V> V propagate(Callable<V> callable){
        try {
            return callable.call();
        } catch (Exception e) {
            throw runtime(e);
        }
    }

    public static void main(String[] args) {
        class Account{
            String name;    
            Account(String name) { this.name = name;}

            public boolean isActive() throws IOException {
                return name.startsWith("a");
            }
        }


        List<Account> accounts = new ArrayList<>(Arrays.asList(new Account("andrey"), new Account("angela"), new Account("pamela")));

        Stream<Account> s = accounts.stream();

        s
          .filter(a -> propagate(a::isActive))
          .map(a -> a.name)
          .forEach(System.out::println);
    }
}

22

Bu UtilExceptionyardımcı sınıf, Java akışlarında işaretli istisnaları şu şekilde kullanmanızı sağlar:

Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
      .map(rethrowFunction(Class::forName))
      .collect(Collectors.toList());

Kontrol edilen not Class::forNameatar . Akışın kendisi de atılır ve bazı kontrol edilmeyen istisna DEĞİLDİR.ClassNotFoundExceptionClassNotFoundException

public final class UtilException {

@FunctionalInterface
public interface Consumer_WithExceptions<T, E extends Exception> {
    void accept(T t) throws E;
    }

@FunctionalInterface
public interface BiConsumer_WithExceptions<T, U, E extends Exception> {
    void accept(T t, U u) throws E;
    }

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

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

@FunctionalInterface
public interface Runnable_WithExceptions<E extends Exception> {
    void run() throws E;
    }

/** .forEach(rethrowConsumer(name -> System.out.println(Class.forName(name)))); or .forEach(rethrowConsumer(ClassNameUtil::println)); */
public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) throws E {
    return t -> {
        try { consumer.accept(t); }
        catch (Exception exception) { throwAsUnchecked(exception); }
        };
    }

public static <T, U, E extends Exception> BiConsumer<T, U> rethrowBiConsumer(BiConsumer_WithExceptions<T, U, E> biConsumer) throws E {
    return (t, u) -> {
        try { biConsumer.accept(t, u); }
        catch (Exception exception) { throwAsUnchecked(exception); }
        };
    }

/** .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) { throwAsUnchecked(exception); return null; }
        };
    }

/** rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))), */
public static <T, E extends Exception> Supplier<T> rethrowSupplier(Supplier_WithExceptions<T, E> function) throws E {
    return () -> {
        try { return function.get(); }
        catch (Exception exception) { throwAsUnchecked(exception); return null; }
        };
    }

/** uncheck(() -> Class.forName("xxx")); */
public static void uncheck(Runnable_WithExceptions t)
    {
    try { t.run(); }
    catch (Exception exception) { throwAsUnchecked(exception); }
    }

/** uncheck(() -> Class.forName("xxx")); */
public static <R, E extends Exception> R uncheck(Supplier_WithExceptions<R, E> supplier)
    {
    try { return supplier.get(); }
    catch (Exception exception) { throwAsUnchecked(exception); return null; }
    }

/** uncheck(Class::forName, "xxx"); */
public static <T, R, E extends Exception> R uncheck(Function_WithExceptions<T, R, E> function, T t) {
    try { return function.apply(t); }
    catch (Exception exception) { throwAsUnchecked(exception); return null; }
    }

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

}

Nasıl kullanılacağı ile ilgili diğer birçok örnek (statik olarak içe aktardıktan sonra UtilException):

@Test
public void test_Consumer_with_checked_exceptions() throws IllegalAccessException {
    Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
          .forEach(rethrowConsumer(className -> System.out.println(Class.forName(className))));

    Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
          .forEach(rethrowConsumer(System.out::println));
    }

@Test
public void test_Function_with_checked_exceptions() throws ClassNotFoundException {
    List<Class> classes1
          = Stream.of("Object", "Integer", "String")
                  .map(rethrowFunction(className -> Class.forName("java.lang." + className)))
                  .collect(Collectors.toList());

    List<Class> classes2
          = Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
                  .map(rethrowFunction(Class::forName))
                  .collect(Collectors.toList());
    }

@Test
public void test_Supplier_with_checked_exceptions() throws ClassNotFoundException {
    Collector.of(
          rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))),
          StringJoiner::add, StringJoiner::merge, StringJoiner::toString);
    }

@Test    
public void test_uncheck_exception_thrown_by_method() {
    Class clazz1 = uncheck(() -> Class.forName("java.lang.String"));

    Class clazz2 = uncheck(Class::forName, "java.lang.String");
    }

@Test (expected = ClassNotFoundException.class)
public void test_if_correct_exception_is_still_thrown_by_method() {
    Class clazz3 = uncheck(Class::forName, "INVALID");
    }

Ancak aşağıdaki avantajları, dezavantajları ve sınırlamaları anlamadan kullanmayın :

• Eğer arama kodu işaretli istisnayı işleyecekse, akışı içeren yöntemin atma maddesine eklemeniz GEREKİR. Derleyici sizi artık eklemeye zorlamayacak, bu yüzden unutmak daha kolay.

Eğer arama kodu kontrol edilen istisnayı zaten ele alıyorsa, derleyici akışı içeren yöntem bildirimine atarma maddesini eklemenizi hatırlatacaktır (eğer söylemezseniz: İstisna asla karşılık gelen try ifadesinin gövdesine atılmaz) ).

• Her durumda, akışı içeren yöntemin İÇİNDE, denetlenen özel durumu yakalamak için akışın kendisini kuşatamazsınız (denerseniz, derleyici şunu söyler: İstisna asla karşılık gelen try ifadesinin gövdesine atılmaz).

• Asla beyan ettiği istisnayı hiçbir zaman atamayacak bir yöntem çağırıyorsanız, bu durumda atma cümleciklerini eklememelisiniz. Örneğin: yeni Dize (byteArr, "UTF-8") UnsupportedEncodingException oluşturur, ancak UTF-8'in Java spec tarafından her zaman mevcut olması garanti edilir. Burada, fırlatma bildirimi bir sıkıntıdır ve minimal kazan plakası ile susturmak için herhangi bir çözüm kabul edilir.

• İşaretli istisnalardan nefret ediyorsanız ve başlamak için Java diline asla eklenmemeleri gerektiğini düşünüyorsanız (giderek artan sayıda insan bu şekilde düşünüyor ve ben onlardan biri DEĞİLİM), o zaman işaretli istisnayı akışı içeren yöntemin yan tümcesini atar. Kontrol edilen istisna, tıpkı Kontrol Edilmemiş bir istisna gibi davranacaktır.

• Bir atış bildirimi ekleme seçeneğinizin bulunmadığı ve yine de bir istisna atmanın tamamen uygun olduğu katı bir arabirim uyguluyorsanız, yalnızca istisna atma ayrıcalığını sahtekarlık istisnalarıyla sonuçlandırmak için bir istisna sarmak aslında neyin yanlış gittiğine dair hiçbir bilgi içermiyor. Buna iyi bir örnek, kontrol edilen istisnaları atmayan Runnable.run () şeklindedir. Bu durumda, işaretli özel durumu akışı içeren yöntemin atarma yan tümcesine eklememeye karar verebilirsiniz.

• Her durumda, akışı içeren yöntemin atar yan tümcesine işaretli istisnayı eklememeye (veya eklemeyi unutmamaya) karar verirseniz, CHECKED istisnalarını atmanın şu 2 sonucunun farkında olun:

1) Arama kodu adı ile yakalayamaz (denerseniz, derleyici şunu söyleyecektir: İstisna asla karşılık gelen try ifadesinin gövdesine atılmaz). Kabarcık olacak ve muhtemelen ana program döngüsünde bazı "İstisna yakalamak" veya "Throwable yakalamak" tarafından yakalanır, bu da istediğiniz gibi olabilir.

2) En az sürpriz ilkesini ihlal eder: artık tüm olası istisnaları yakalamayı garanti edebilmek için RuntimeException'ı yakalamak yeterli olmayacaktır. Bu nedenle, bunun çerçeve kodunda değil, yalnızca tamamen kontrol ettiğiniz iş kodunda yapılması gerektiğine inanıyorum.

Sonuç olarak: Buradaki sınırlamaların ciddi olmadığına ve UtilExceptionsınıfın korkusuzca kullanılabileceğine inanıyorum. Ancak, size kalmış!


8

StreamDenetlenmeyen bir istisna atmak için lambda'nızı sararak ve daha sonra terminal işlemlerinde bu denetlenmeyen istisnayı kaldırarak kendi varyantınızı potansiyel olarak yuvarlayabilirsiniz :

@FunctionalInterface
public interface ThrowingPredicate<T, X extends Throwable> {
    public boolean test(T t) throws X;
}

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

@FunctionalInterface
public interface ThrowingSupplier<R, X extends Throwable> {
    public R get() throws X;
}

public interface ThrowingStream<T, X extends Throwable> {
    public ThrowingStream<T, X> filter(
            ThrowingPredicate<? super T, ? extends X> predicate);

    public <R> ThrowingStream<T, R> map(
            ThrowingFunction<? super T, ? extends R, ? extends X> mapper);

    public <A, R> R collect(Collector<? super T, A, R> collector) throws X;

    // etc
}

class StreamAdapter<T, X extends Throwable> implements ThrowingStream<T, X> {
    private static class AdapterException extends RuntimeException {
        public AdapterException(Throwable cause) {
            super(cause);
        }
    }

    private final Stream<T> delegate;
    private final Class<X> x;

    StreamAdapter(Stream<T> delegate, Class<X> x) {
        this.delegate = delegate;
        this.x = x;
    }

    private <R> R maskException(ThrowingSupplier<R, X> method) {
        try {
            return method.get();
        } catch (Throwable t) {
            if (x.isInstance(t)) {
                throw new AdapterException(t);
            } else {
                throw t;
            }
        }
    }

    @Override
    public ThrowingStream<T, X> filter(ThrowingPredicate<T, X> predicate) {
        return new StreamAdapter<>(
                delegate.filter(t -> maskException(() -> predicate.test(t))), x);
    }

    @Override
    public <R> ThrowingStream<R, X> map(ThrowingFunction<T, R, X> mapper) {
        return new StreamAdapter<>(
                delegate.map(t -> maskException(() -> mapper.apply(t))), x);
    }

    private <R> R unmaskException(Supplier<R> method) throws X {
        try {
            return method.get();
        } catch (AdapterException e) {
            throw x.cast(e.getCause());
        }
    }

    @Override
    public <A, R> R collect(Collector<T, A, R> collector) throws X {
        return unmaskException(() -> delegate.collect(collector));
    }
}

Daha sonra bunu aşağıdakilerle aynı şekilde kullanabilirsiniz Stream:

Stream<Account> s = accounts.values().stream();
ThrowingStream<Account, IOException> ts = new StreamAdapter<>(s, IOException.class);
return ts.filter(Account::isActive).map(Account::getNumber).collect(toSet());

Bu çözüm biraz kazan levhası gerektirecektir, bu yüzden zaten tüm Streamsınıf (ve daha fazlası!) İçin burada tarif ettiğim şeyi yapan zaten yaptığım kütüphaneye göz atmanızı öneririm .


Merhaba ... küçük böcek? new StreamBridge<>(ts, IOException.class);->new StreamBridge<>(s, IOException.class);
kevinarpe

1
@kevinarpe Yep. Ayrıca söylemeliydim StreamAdapter.
Jeffrey

5

#Propagate () yöntemini kullanın. Sam Beran'ın Java 8 Blogundan Guava olmayan örnek uygulama :

public class Throwables {
    public interface ExceptionWrapper<E> {
        E wrap(Exception e);
    }

    public static <T> T propagate(Callable<T> callable) throws RuntimeException {
        return propagate(callable, RuntimeException::new);
    }

    public static <T, E extends Throwable> T propagate(Callable<T> callable, ExceptionWrapper<E> wrapper) throws E {
        try {
            return callable.call();
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw wrapper.wrap(e);
        }
    }
}

Java 8 blog bağlantısı öldü.
Spycho

4

Bu soruya doğrudan cevap vermez (yapacak başka cevaplar da vardır) ancak ilk etapta problemden kaçınmaya çalışır:

Benim tecrübelerime göre, istisnaları bir Stream(veya başka bir lambda ifadesinde) ele alma ihtiyacı , istisnaların atılmamaları gereken yöntemlerden atıldığı beyan edilmesinden kaynaklanmaktadır. Bu genellikle iş mantığını giriş ve çıkışla karıştırmaktan gelir. Sizin Accountarayüzü mükemmel bir örnektir:

interface Account {
    boolean isActive() throws IOException;
    String getNumber() throws IOException;
}

IOExceptionHer alıcıya bir atmak yerine, bu tasarımı düşünün:

interface AccountReader {
    Account readAccount(…) throws IOException;
}

interface Account {
    boolean isActive();
    String getNumber();
}

Yöntem AccountReader.readAccount(…), bir veritabanından veya dosyadan veya her neyse bir hesabı okuyabilir ve başarılı olmazsa bir istisna atabilir. AccountKullanılmaya hazır tüm değerleri içeren bir nesne oluşturur. Değerler önceden yüklendiği readAccount(…)için, alıcılar bir istisna atmaz. Böylece, istisnaları sarmaya, maskelemeye veya gizlemeye gerek kalmadan lambdalarda özgürce kullanabilirsiniz.

Tabii ki bunu tanımladığım şekilde yapmak her zaman mümkün değildir, ancak çoğu zaman budur ve daha temiz kodlara yol açar (IMHO):

  • Endişelerin daha iyi ayrılması ve tek sorumluluk ilkesinin uygulanması
  • Daha az kaynak plakası: throws IOExceptionDerleyiciyi tatmin etmek için kullanmanız gerekmeden kodunuzu karıştırmanız gerekmez
  • Hata işleme: Yalnızca bir alan değeri almak istediğiniz için iş mantığınızın ortasında bir yer yerine - bir dosyadan veya veritabanından okurken - oluştukları hataları işlersiniz
  • Account Değişmez hale getirebilir ve avantajlarından faydalanabilirsiniz (örn. İplik güvenliği)
  • AccountLambdalarda kullanmak için "kirli numaralara" veya geçici çözümlere ihtiyacınız yoktur (örneğin a Stream)

4

Bu basit kodunun altına çözülebilir Çayı'nın ve deneyin içinde AbacusUtil :

Stream.of(accounts).filter(a -> Try.call(a::isActive)).map(a -> Try.call(a::getNumber)).toSet();

Açıklama : Ben geliştiriciyim AbacusUtil.


3

@Marcg çözümünü genişleterek, normal olarak Streams'te işaretli bir istisna atabilir ve yakalayabilirsiniz ; yani, derleyici akışları dışında olduğu gibi yakalamak / yeniden atmak isteyecektir !

@FunctionalInterface
public interface Predicate_WithExceptions<T, E extends Exception> {
    boolean test(T t) throws E;
}

/**
 * .filter(rethrowPredicate(t -> t.isActive()))
 */
public static <T, E extends Exception> Predicate<T> rethrowPredicate(Predicate_WithExceptions<T, E> predicate) throws E {
    return t -> {
        try {
            return predicate.test(t);
        } catch (Exception exception) {
            return throwActualException(exception);
        }
    };
}

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

Ardından, örneğiniz aşağıdaki gibi yazılacaktır (daha net göstermek için testler ekleyerek):

@Test
public void testPredicate() throws MyTestException {
    List<String> nonEmptyStrings = Stream.of("ciao", "")
            .filter(rethrowPredicate(s -> notEmpty(s)))
            .collect(toList());
    assertEquals(1, nonEmptyStrings.size());
    assertEquals("ciao", nonEmptyStrings.get(0));
}

private class MyTestException extends Exception { }

private boolean notEmpty(String value) throws MyTestException {
    if(value==null) {
        throw new MyTestException();
    }
    return !value.isEmpty();
}

@Test
public void testPredicateRaisingException() throws MyTestException {
    try {
        Stream.of("ciao", null)
                .filter(rethrowPredicate(s -> notEmpty(s)))
                .collect(toList());
        fail();
    } catch (MyTestException e) {
        //OK
    }
}

Bu örnek derlenmemektedir
Roman M

Merhaba @RomanM bunu işaret ettiğiniz için teşekkür ederiz: yöntem "throwActualException" eksik dönüş türü düzelttim. Bunu üretimde kullanıyoruz, umarım sizin tarafınızda da çalışır.
PaoloC

3

IOException (RuntimeException'a) işleme kodunu düzgün bir şekilde eklemek için, yönteminiz şöyle görünecektir:

Stream<Account> s =  accounts.values().stream();

s = s.filter(a -> { try { return a.isActive(); } 
  catch (IOException e) { throw new RuntimeException(e); }});

Stream<String> ss = s.map(a -> { try { return a.getNumber() }
  catch (IOException e) { throw new RuntimeException(e); }});

return ss.collect(Collectors.toSet());

Şimdi sorun IOException, bir olarak yakalanması RuntimeExceptionve bir IOException- haline dönüştürülmesi gerekecek ve bu da yukarıdaki yönteme daha fazla kod ekleyecektir.

Neden Streambu şekilde yapılabileceği zaman kullanılır - ve yöntem atar, IOExceptionböylece bunun için fazladan kod gerekmez:

Set<String> set = new HashSet<>();
for(Account a: accounts.values()){
  if(a.isActive()){
     set.add(a.getNumber());
  } 
}
return set;

1

Bu konuyu akılda tutarak, kontrol edilen istisnalar ve lambdalarla uğraşmak için küçük bir kütüphane geliştirdim. Özel bağdaştırıcılar, mevcut işlevsel türlerle entegre olmanızı sağlar:

stream().map(unchecked(URI::new)) //with a static import

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


1

Örneğiniz şu şekilde yazılabilir:

import utils.stream.Unthrow;

class Bank{
   ....
   public Set<String> getActiveAccountNumbers() {
       return accounts.values().stream()
           .filter(a -> Unthrow.wrap(() -> a.isActive()))
           .map(a -> Unthrow.wrap(() -> a.getNumber()))
           .collect(Collectors.toSet());
   }
   ....
}

Unthrow sınıf buradan alınabilir https://github.com/SeregaLBN/StreamUnthrower



0

Java'daki işlevsel arabirimler, denetlenen veya denetlenmeyen bir özel durum bildirmez. Yöntemlerin imzasını şu şekilde değiştirmemiz gerekir:

boolean isActive() throws IOException; 
String getNumber() throwsIOException;

Kime:

boolean isActive();
String getNumber();

Veya try-catch bloğu ile kullanın:

public Set<String> getActiveAccountNumbers() {
  Stream<Account> s =  accounts.values().stream();
  s = s.filter(a -> 
    try{
      a.isActive();
    }catch(IOException e){
      throw new RuntimeException(e);
    }
  );
  Stream<String> ss = s.map(a -> 
    try{
      a.getNumber();
    }catch(IOException e){
      throw new RuntimeException(e);
    }
  );
  return ss.collect(Collectors.toSet());
}

Başka bir seçenek de özel bir sargı yazmak veya ThrowingFunction gibi bir kitaplık kullanmaktır. Kütüphane ile sadece pom.xml'ye bağımlılığı eklememiz gerekir:

<dependency>
    <groupId>pl.touk</groupId>
    <artifactId>throwing-function</artifactId>
    <version>1.3</version>
</dependency>

Ve ThrowingFunction, ThrowingConsumer, ThrowingPredicate, ThrowingRunnable, ThrowingSupplier gibi belirli sınıfları kullanın.

Sonunda kod şöyle görünür:

public Set<String> getActiveAccountNumbers() {
  return accounts.values().stream()
    .filter(ThrowingPredicate.unchecked(Account::isActive))
    .map(ThrowingFunction.unchecked(Account::getNumber))
    .collect(Collectors.toSet());
}

0

Akış (Java-8) kontrol edilmiş istisna işleme herhangi bir yol görmüyorum, uyguladığım tek yolu akışı kontrol denetimli istisna yakalamak ve denetlenmeyen istisna olarak yeniden atmak.

        Arrays.stream(VERSIONS)
        .map(version -> TemplateStore.class
                .getClassLoader().getResourceAsStream(String.format(TEMPLATE_FILE_MASK, version)))
        .map(inputStream -> {
            try {
                return ((EdiTemplates) JAXBContext.newInstance(EdiTemplates.class).createUnmarshaller()
                        .unmarshal(inputStream)).getMessageTemplate();
            } catch (JAXBException e) {
                throw new IllegalArgumentException(ERROR, e);
            }})
        .flatMap(Collection::stream)
        .collect(Collectors.toList());

Aslında soruyu cevaplamaya mı çalışıyorsun?
Nilambar Sharma

@Nilambar - Ben burada söylemeye çalışıyorum, java akışı kullanırken kontrol edilen istisna işlemek için hiçbir yolu yoktur ... her şeyi sonunda biz kontrol birini yakalamak ve çalışma zamanı / kontrolsüz bir atmak gerekir .. Şimdi iki şey vardır, 1. benim anlayış ile doğru olmadığını düşünüyorsanız lütfen beni düzeltin Veya 2. düşünüyorsanız, benim yazı ilgisiz, ben de kaldırmak için mutluyum. Saygılarımla, Atul
atul sachan

0

Akıştaki istisnayı ele almak ve ek olanları işlemeye devam etmek istiyorsanız, Brian Vermeer tarafından İkisinden biri kavramını kullanan mükemmel bir makale var . Bu durumu ele almanın mükemmel bir yolunu gösterir. Eksik olan tek şey örnek koddur. Bu, o makaledeki kavramları kullanarak yaptığım araştırmanın bir örneğidir.

@Test
public void whenValuePrinted_thenPrintValue() {

    List<Integer> intStream = Arrays.asList(0, 1, 2, 3, 4, 5, 6);
    intStream.stream().map(Either.liftWithValue(item -> doSomething(item)))
             .map(item -> item.isLeft() ? item.getLeft() : item.getRight())
             .flatMap(o -> {
                 System.out.println(o);
                 return o.isPresent() ? Stream.of(o.get()) : Stream.empty();
             })
             .forEach(System.out::println);
}

private Object doSomething(Integer item) throws Exception {

    if (item == 0) {
        throw new Exception("Zero ain't a number!");
    } else if (item == 4) {
        return Optional.empty();
    }

    return item;
}
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.