Java tarafından kontrol edilen istisnalar için geçici çözüm


49

Java 8'in lambdalar ve varsayılan yöntemler arabirimleriyle ilgili özelliklerini çok takdir ediyorum. Yine de, kontrol edilen istisnalar dışında hala sıkıldım. Örneğin, bir nesnenin tüm görünür alanlarını listelemek istersem, şunu yazmak istiyorum:

    Arrays.asList(p.getClass().getFields()).forEach(
        f -> System.out.println(f.get(p))
    );

Bununla birlikte, getyöntem, Consumerarayüz sözleşmesini kabul etmeyen, işaretli bir istisna atabildiğinden , bu istisnayı yakalayıp aşağıdaki kodu yazmam gerekir:

    Arrays.asList(p.getClass().getFields()).forEach(
            f -> {
                try {
                    System.out.println(f.get(p));
                } catch (IllegalArgumentException | IllegalAccessException ex) {
                    throw new RuntimeException(ex);
                }
            }
    );

Ancak çoğu durumda sadece istisnanın atılmasını istiyorum ve RuntimeExceptionderleme hatası olmadan istisnayı programın işlemesine izin verip vermemeyi istiyorum.

Bu nedenle, kontrol edilen istisnalar sıkıntısı konusundaki tartışmalı geçici çözümüm hakkında fikrinizi almak istiyorum. Bu amaçla, yardımcı bir arayüz ConsumerCheckException<T>ve bir yardımcı fonksiyon oluşturdum rethrow( Doval'ın yorumunun önerisine göre güncellendi ):

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

  public class Wrappers {
      public static <T> Consumer<T> rethrow(ConsumerCheckException<T> c) {
        return elem -> {
          try {
            c.accept(elem);
          } catch (Exception ex) {
            /**
             * within sneakyThrow() we cast to the parameterized type T. 
             * In this case that type is RuntimeException. 
             * At runtime, however, the generic types have been erased, so 
             * that there is no T type anymore to cast to, so the cast
             * disappears.
             */
            Wrappers.<RuntimeException>sneakyThrow(ex);
          }
        };
      }

      /**
       * Reinier Zwitserloot who, as far as I know, had the first mention of this
       * technique in 2009 on the java posse mailing list.
       * http://www.mail-archive.com/javaposse@googlegroups.com/msg05984.html
       */
      public static <T extends Throwable> T sneakyThrow(Throwable t) {
          throw (T) t;
      }
  }

Ve şimdi sadece yazabilirim:

    Arrays.asList(p.getClass().getFields()).forEach(
            rethrow(f -> System.out.println(f.get(p)))
    );

Bunun, kontrol edilen istisnalar dışında kalan en iyi deyim olduğundan emin değilim, ancak açıkladığım gibi, kontrol edilen istisnalarla uğraşmadan ilk örneğime ulaşmak için daha uygun bir yol istiyorum ve bu bulduğum en basit yol. yapmak için.



2
Robert'in bağlantısına ek olarak, Sneakily Throwing Checked Exceptions'a bir göz atın . İsterseniz, sneakyThrowiçine rethrowsarmak yerine orijinali işaretli istisnayı atmak için içini kullanabilirsiniz RuntimeException. Alternatif olarak , aynı şeyi @SneakyThrowsyapan Project Lombok'tan ek açıklama kullanabilirsiniz .
Doval

1
Ayrıca, Consumers'nin forEachparalel Streams kullanılırken paralel bir şekilde yürütülebileceğini unutmayın . Tüketici ile birlikte atılmaktan atılan bir fırlatma, daha sonra, 1) diğer eşzamanlı çalışan tüketicileri durduramayacak, uygun olabilecek veya uygun olmayacak, ve 2) tüketicilerin birden fazla bir şeyi atması durumunda, Atılabilir eşyalardan biri çağıran iplik tarafından görülecektir.
Joonas Pulakka


2
Lambdalar çok çirkin.
Tulains Córdova

Yanıtlar:


16

Tekniğinizin avantajları, dezavantajları ve kısıtlamaları:

  • Çağıran kod, kontrol edilen istisnaları işlemekse, akışı içeren yöntemin throws deyimine eklemeniz GEREKİR. Derleyici sizi artık eklemeye zorlamaz, bu yüzden unutmak daha kolaydır. Örneğin:

    public void test(Object p) throws IllegalAccessException {
        Arrays.asList(p.getClass().getFields()).forEach(rethrow(f -> System.out.println(f.get(p))));
    }
    
  • Çağıran kod zaten kontrol edilen istisnayı ele alıyorsa, derleyici, derleme işlemini, akımı içeren yöntem bildirimine atma yan tümcesini eklemenizi hatırlatır. .

  • Her durumda, kontrol edilen istisnayı yakalamak için akışın kendisini kuşatamazsınız (akışı deneyen yöntemin INSIDE) (denerseniz derleyici şunları söyler: İstisna asla karşılık gelen try ifadesinin gövdesine atılmaz).

  • Kelimenin tam anlamıyla, bildirdiği istisnayı asla atmayacak bir yöntem çağırıyorsanız, throws deyimini dahil etmemelisiniz. Örneğin: new String (byteArr, "UTF-8"), UnsupportedEncodingException özelliğini atıyor, ancak UTF-8, her zaman bulunacağı Java belirtimi tarafından garanti ediliyor. Burada, fırlatma bildirimi sıkıntı vericidir ve minimal kazan ile susturmanın herhangi bir çözümü memnuniyetle karşılanmaktadır.

  • Denetlenen istisnalardan nefret ediyorsanız ve başlamak için asla Java diline eklenmemeleri gerektiğini hissediyorsanız (gittikçe artan sayıda insan bu şekilde düşünür ve ben onlardan biri değilim), o zaman atılanlara işaretli istisnayı eklemeyin akışı içeren yöntemin yan tümcesi. Kontrol edilen istisna, kontrolsüz bir istisna gibi davranır.

  • Bir fırlatma bildirimi ekleme seçeneğinizin olmadığı ve henüz bir istisna atma seçeneğinin olmadığı katı bir arabirim uyguluyorsanız, yalnızca özel durum atma ayrıcalığını kazanmak için bir istisna paketlemek Neyin yanlış gittiğiyle ilgili hiçbir bilgi vermeyin. İyi bir örnek, kontrol edilen istisnalar atmayan Runnable.run () 'dir. Bu durumda, işaretli istisnayı, akımı içeren yöntemin fırlatma yan tümcesine eklememeye karar verebilirsiniz.

  • Her durumda, işaretli istisnayı, akışı içeren yöntemin atama yan tümcesine eklemeye (ya da eklemeyi unutmaya) karar verirseniz, KONTROLÜ istisnalarını atmanın şu 2 sonucunun farkında olun:

    1. Çağıran kod onu isimle yakalayamayacak (denerseniz derleyici der ki: İstisna asla karşılık gelen try ifadesinin gövdesinde atılmaz). Kabarcıklı ve büyük olasılıkla ana program döngüsünde, bir miktar "İstisna İstisnası" veya "Atılabilir Tutma" ile yakalanacaktır.

    2. En küçük sürpriz ilkesini ihlal ediyor: Tüm olası istisnaları yakalamayı garanti altına almak için RuntimeException'ı yakalamak artık yeterli olmayacak. Bu nedenle, bunun çerçeve kodunda yapılması gerekmediğine inanıyorum, ancak sadece tamamen sizin kontrol ettiğiniz iş kodunda.

Referanslar:

NOT: Bu tekniği kullanmaya karar verirseniz, LambdaExceptionUtilyardımcı sınıfı StackOverflow'tan kopyalayabilirsiniz : https://stackoverflow.com/questions/27644361/how-can-i-throw-checked-exceptions-from-inside-java-8 -Streams . Size örneklerle tam bir uygulama (Fonksiyon, Tüketici, Tedarikçi ...) verir.


6

Bu örnekte, gerçekten başarısız olabilir mi? Öyle düşünmeyin, ama belki de durumunuz özeldir. Eğer gerçekten "başarısız olamaz" ve bu sadece sinir bozucu bir derleyici olayıysa, istisnayı sarmayı ve Error"olamaz" yorumunu atmayı seviyorum . Bakım için işleri çok netleştirir . Aksi takdirde, "bu nasıl olabilir" ve "bunu hallediyor kim" diye merak edeceklerdir.

Bu tartışmalı bir uygulamada yani YMMV. Muhtemelen bazı indirimler alırım.


3
+1 çünkü bir Hata atarsınız. Kaç kez sadece tek satırlı bir yorum içeren "bir olamaz" içeren bir yakalamak bloğunda hata ayıklama yaptıktan sonra sona erdi ...
Axel

3
-1 çünkü bir Hata atarsınız. Hatalar, JVM'de bir şeylerin yanlış olduğunu ve üst seviyelerin de buna göre işleyebileceğini belirtmek içindir. Bu yolu seçerseniz, RuntimeException öğesini atmalısınız. Başka bir olası geçici çözüm, varsayımlar (bir bayrak-etkin bayrak olabilir) veya kayıt olmaktır.
15'te

3
@duros: İstisnanın neden "olamayacağına" bağlı olarak, atıldığı gerçeği bir şeyin ciddi şekilde yanlış olduğunu gösterebilir . Örneğin, birinin desteklediği bilinen clonemühürlü bir türden birini Fooçağırması ve fırlatması varsayalım CloneNotSupportedException. Kod, beklenmedik bir şekilde beklenmedik bir şekilde bağlanmadıkça bu nasıl olabilir Foo? Ve eğer bu olursa, her şey güvenilir olabilir mi?
supercat

1
@supercat mükemmel bir örnek. Diğerleri eksik Dize Kodlamaları veya MessageDigest'lerden istisnalar atıyor UTF-8 veya SHA yoksa, çalışma zamanınız büyük olasılıkla bozulmuş demektir.
user949300,

1
@duros Bu tam bir yanılgıdır. An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch.(Javadoc'dan alıntı Error) Bu tam da böyle bir durum, bu nedenle yetersiz olmaktan, Errorburaya atmak en uygun seçenektir.
biziclop

1

Bu istisnaların sadece kontrol edilen istisnaların geciktiği bir başka versiyonu:

public class Cocoon {
         static <T extends Throwable> T forgetThrowsClause(Throwable t) throws T{
            throw (T) t;
        }

        public static <X, T extends Throwable> Consumer<X> consumer(PeskyConsumer<X,T> touchyConsumer) throws T {
            return new Consumer<X>() {
                @Override
                public void accept(X t) {
                    try {
                        touchyConsumer.accept(t);
                    } catch (Throwable exc) {
                        Cocoon.<RuntimeException>forgetThrowsClause(exc) ;
                    }

                }
            } ;
        }
// and so on for Function, and other codes from java.util.function
}

sihir şöyle diyorsan:

 myArrayList.forEach(Cocoon.consumer(MyClass::methodThatThrowsException)) ;

o zaman kodunuz istisnayı yakalamakla yükümlü olacak


Elementlerin farklı dişlerde kullanıldığı bir paralel akışta çalıştırılırsa ne olur?
Ocak'ta

0

sinsi Atmak havalı! Acını tamamen hissediyorum.

Java'da kalmak zorundaysan ...

Paguro, kontrol edilen istisnaları kaplayan işlevsel arayüzlere sahiptir, böylece artık bunu düşünmenize gerek kalmaz. İşlevsel arayüzleri alan ve kontrol edilen istisnaları saran Clojure transdüserlerinin (veya Java 8 Streams) hatları boyunca değişmeyen koleksiyonları ve fonksiyonel dönüşümleri içerir.

Orada da VAVr ve Eclipse Koleksiyonları denemek için.

Aksi takdirde, Kotlin'i kullanın.

Kotlin , Java ile 2 yönlü uyumludur, kontrol edilen istisnalar yok, ilkeller yok (iyi, programcının düşünmesi gerekmiyor), daha iyi bir tür sistem var, değişmezliği üstleniyor, vs. m tüm Java kodlarını Kotlin'e çeviriyorum. Java'da yaptığım her şeyi şimdi Kotlin'de yapmayı tercih ediyorum.


Biri buna oy vermiş, bu iyi, ama bana neden oy kullandığını söylemedikçe hiçbir şey öğrenmeyeceğim .
GlenPeterson

1
Github'daki kütüphaneniz için +1, ayrıca eclipse.org/collections adresini veya işlevsel ve değişken koleksiyonlar sağlayan project vavr dosyasını da kontrol edebilirsiniz . Bu konudaki düşünceleriniz neler?
firephil

-1

Kontrol edilen istisnalar çok kullanışlıdır ve kullanım kuralları, kodladığınız ürünün türüne bağlıdır:

  • bir kütüphane
  • bir masaüstü uygulaması
  • bir kutu içinde çalışan bir sunucu
  • akademik bir alıştırma

Genellikle bir kütüphane, kontrol edilen istisnaları yerine getirmemeli, ancak genel API tarafından atıldığı gibi beyan etmelidir. Örneğin, düz RuntimeExcepton uygulamasına eklenebilecek nadir durum, örneğin, bazı özel xml belgelerinin kitaplık kodunun içinde oluşturulması ve ayrıştırılması, geçici bir dosya oluşturulması ve daha sonra okunmasıdır.

Masaüstü uygulamaları için, istisna işleme çerçevenizi oluşturarak ürün geliştirmeye başlamanız gerekir. Kendi çerçevenizi kullanarak, işaretli bir istisnayı iki ila dört paketleyiciden bazılarına (özel RuntimeExcepion alt sınıflarınız) saracak ve bunları tek bir Özel Durum İşleyicisi ile ele alacaksınız.

Sunucular için kodunuz bazı çerçevelerin içinde çalışacaktır. Herhangi bir (çıplak sunucu) kullanmıyorsanız, yukarıda açıklandığı gibi (Runtimes'a göre) kendi çerçevenizi oluşturun.

Akademik egzersizler için bunları doğrudan RuntimeExcepton'a doğrudan sarın. Birileri de küçük bir çerçeve yaratabilir.


-1

Bu projeye bakmak isteyebilirsiniz ; Özel olarak, kontrol edilen istisnalar ve fonksiyonel arayüzler sorunu nedeniyle yarattım.

Bununla özel kodunuzu yazmak yerine bunu yapabilirsiniz:

// I don't know the type of f, so it's Foo...
final ThrowingConsumer<Foo> consumer = f -> System.out.println(f.get(p));

Tüm bu Throwing * arayüzleri, Throwing olmayan rakiplerini genişlettiğinden, bunları doğrudan akışta kullanabilirsiniz:

Arrays.asList(p.getClass().getFields()).forEach(consumer);

Ya da sadece:

import static com.github.fge.lambdas.consumers.Consumers.wrap;

Arrays.asList(p.getClass().getFields())
    .forEach(wrap(f -> System.out.println(f.get(p)));

Ve diğer şeyler.


Hala daha iyifields.forEach((ThrowingConsumer<Field>) f -> out.println(f.get(o)))
user2418306

Katılanlar lütfen açıklar mı?
Fge
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.