Kontrol edilen istisnalara karşı dava


456

Birkaç yıldır şu soruya iyi bir cevap alamıyorum: neden bazı geliştiriciler kontrol edilmiş istisnalara karşı bu kadar başarılı? Çok sayıda sohbet ettim, bloglarda bir şeyler okudum, Bruce Eckel'in söylediklerini okudum (gördüğüm ilk kişi onlara karşı konuştu).

Şu anda bazı yeni kodlar yazıyorum ve istisnalarla nasıl başa çıktığım konusunda çok dikkatli davranıyorum. "Kontrol edilen istisnaları sevmiyoruz" kalabalığının bakış açısını görmeye çalışıyorum ve hala göremiyorum.

Yaptığım her konuşma aynı soruya cevapsız olarak bitiyor ... ayarlayayım:

Genel olarak (Java'nın nasıl tasarlandığından),

  • Error asla yakalanmaması gereken şeyler içindir (VM'nin fıstık alerjisi vardır ve birisi üzerine bir kavanoz fıstık bırakmıştır)
  • RuntimeException programcının yanlış yaptığı şeyler içindir (programcı bir dizinin sonundan yürüdü)
  • Exception (dışında RuntimeException ) programlayıcının kontrolü dışında olan şeyler içindir (dosya sistemine yazılırken disk dolar, işlem için dosya tanıma sınırına ulaşıldı ve başka dosya açamazsınız)
  • Throwable tüm istisna türlerinin üstüdür.

Duyduğum yaygın bir argüman, bir istisna meydana gelirse, geliştiricinin yapacağı tüm programdan çıkmaktır.

Duyduğum bir diğer yaygın argüman, kontrol edilen istisnaların kodu yeniden düzenlemeyi zorlaştırıyor.

"Tüm yapacağım çıkış" argümanı için, çıksanız bile makul bir hata mesajı görüntülemeniz gerektiğini söylüyorum. Sadece hataları ele alıyorsanız, programın nedenini açıkça belirtmeden çıktığı zaman kullanıcılarınız aşırı derecede mutlu olmayacaktır.

"Yeniden düzenlemeyi zorlaştırır" kalabalık için, uygun soyutlama düzeyinin seçilmediğini gösterir. Bir yöntem beyan etmek yerine IOException,IOException bitenler için daha uygun bir istisna haline dönüştürülmelidir.

Main'i kaydırmakla ilgili bir sorunum yok catch(Exception)(veya bazı durumlarda catch(Throwable)programın zarif bir şekilde çıkabilmesini sağlamak için - ama her zaman ihtiyacım olan özel istisnaları yakalarım. hata mesajı.

İnsanların hiç cevap vermediği soru şudur:

Eğer atarsanız RuntimeException yerine alt sınıflarını Exception alt sınıfları daha sonra nasıl sen catch gerekiyor biliyor musunuz?

Cevap yakalanıyorsa Exception , sistem istisnalarıyla aynı şekilde programcı hatalarıyla da uğraşıyorsunuz demektir. Bu benim için yanlış görünüyor.

Eğer yakalarsam Throwableo zaman sistem istisnalar ve VM hataları (ve benzeri) aynı şekilde muamele edilir. Bu benim için yanlış görünüyor.

Eğer cevap sadece bildiğiniz istisnaları yakalarsanız, hangilerinin atıldığını nasıl anlarsınız? Programcı X yeni bir istisna atar ve onu yakalamayı unutursa ne olur? Bu benim için çok tehlikeli görünüyor.

Yığın izleme görüntüleyen bir programın yanlış olduğunu söyleyebilirim. İşaretli istisnaları sevmeyen insanlar bu şekilde hissetmiyor mu?

Öyleyse, kontrol edilen istisnaları sevmiyorsanız, neden yanıtlanmadığını VE cevaplanmayan soruya cevap veremediğinizi açıklayabilir misiniz?

Düzenleme: Ben ne zaman her iki modelini kullanmak için tavsiye aramıyorum, ne aradığım insanlar neden uzanıyor RuntimeExceptionsevmiyorum Exceptionve / veya neden bir istisna yakalamak ve sonra RuntimeExceptionatar eklemek yerine bir yeniden rethrow neden onların yöntemi. İşaretli istisnaları sevmeme motivasyonunu anlamak istiyorum.


46
Bunun tamamen öznel olduğunu düşünmüyorum - bu, herkesin kendileri için neye karar vermesinden ziyade, belirli bir kullanım için tasarlanmış bir dil özelliğidir. Ve özellikle tartışmacı değil, önceden insanların kolayca ortaya koyabileceği belirli çürütmeleri ele alıyor.
Gareth

6
Haydi. Bir dil özelliği olarak görülen bu konu nesnel bir şekilde ele alınmıştır ve yaklaşılabilir.
Kurt Schelfthout

6
@cletus "kendi sorunuzu cevaplıyor" cevabı olsaydı soruyu sormazdım!
TofuBeer

8
Harika bir soru. C ++ 'da kontrol edilen istisna yoktur ve bence istisna özelliğini kullanılamaz hale getirir. Yaptığınız her bir işlev çağrısının etrafına yakalamak zorunda olduğunuz bir durumla sonuçlanırsınız, çünkü sadece bir şey fırlatabileceğini bilmiyorsunuz.
Dimitri C.Haziran

4
İşaretli istisnalar için bildiğim en güçlü argüman , başlangıçta Java'da bulunmadıkları ve tanıtıldıklarında JDK'da yüzlerce hata keşfettikleri. Bu, Java 1.0'dan biraz önce. Şahsen onlarsız olmazdım ve Bruce Eckel ve bu konuda başkalarına şiddetle katılmıyorum.
Lorne Marquis

Yanıtlar:


277

Sanırım aynı Bruce Eckel röportajını okudum - ve her zaman beni rahatsız etti. Aslında, argüman görüşmeci tarafından yapılmıştır (eğer gerçekten bahsettiğiniz gönderi ise) .NET ve C #'ın ardındaki MS dehası Anders Hejlsberg.

http://www.artima.com/intv/handcuffs.html

Fan, ben Hejlsberg ve eseri olmamasına rağmen, bu argüman beni her zaman sahte olarak vurdu. Temel olarak aşağıdakilere kadar kaynar:

"Kontrol edilen istisnalar kötü çünkü programcılar onları her zaman yakalayarak ve onları işten çıkararak kötüye kullanıyorlar, bu da kullanıcıya aksi halde sunulacak olan gizli ve göz ardı edilen sorunlara yol açıyor".

Tarafından "aksi Kullanıcıya sunulan" Bir çalışma zamanı özel kullanırsanız Yani tembel programcı sadece (boş bir catch bloğu ile alıcı yerine) göz ardı eder ve kullanıcı bunu görecek.

Argümanın özetinin özeti, "Programcılar bunları düzgün kullanmaz ve düzgün kullanmamak, sahip olmamaktan daha kötüdür" dir. .

Bu argümanın bir gerçeği var ve aslında, Java'da operatör geçersiz kılmalarını koymamak için Goslings motivasyonunun benzer bir argümandan geldiğinden şüpheleniyorum - programcıyı karıştırıyorlar çünkü sık sık istismar ediliyorlar.

Ama sonunda, bunu Hejlsberg'in ve muhtemelen iyi düşünülmüş bir karardan ziyade eksikliği açıklamak için yaratılan post-hoc bir argüman buldum.

Kontrol edilen istisnaların aşırı kullanımı kötü bir şey olsa da ve kullanıcılar tarafından özensiz işlemeye yol açarken, bunların doğru kullanımının API programcısının API istemci programcısına büyük fayda sağlamasına izin verdiğini iddia ediyorum.

Şimdi API programcısı, kontrol edilmiş istisnaları her yere atmamaya dikkat etmelidir, yoksa sadece istemci programlayıcıyı rahatsız edecektir. Çok tembel müşteri programcısı, (Exception) {}Hejlsberg'in uyardığı gibi yakalamak için başvuracak ve tüm faydalar kaybedilecek ve cehennem ortaya çıkacak. Ancak bazı durumlarda, iyi kontrol edilmiş bir istisnanın yerini tutamaz.

Benim için klasik örnek, dosya açma API'sıdır. Diller tarihindeki her programlama dilinin (en azından dosya sistemlerinde) bir dosyayı açmanıza izin veren bir API'si vardır. Ve bu API'yı kullanan her istemci programcısı, açmaya çalıştıkları dosyanın mevcut olmamasıyla uğraşmak zorunda olduklarını biliyor. Bunu yeniden ifade edeyim: Bu API'yı kullanan her istemci programcısı , bu durumla uğraşmak zorunda olduklarını bilmelidir . Ve bir sürtünme var: API programcısı sadece yorum yaparak başa çıkmaları gerektiğini bilmelerine yardımcı olabilir mi yoksa gerçekten ısrar edebilirler mi? müşteriyle bununla başa çıkmak mi?

C'de deyim şuna benzer

  if (f = fopen("goodluckfindingthisfile")) { ... } 
  else { // file not found ...

burada fopen0 ve C döndürerek başarısızlığı gösterir (aptalca) 0'a bir boole gibi davranmanıza izin verir ve ... Temel olarak, bu deyimi öğrenirsiniz ve iyisinizdir. Ama ya bir çaylaksanız ve deyimi öğrenmediyseniz. Sonra, elbette,

   f = fopen("goodluckfindingthisfile");
   f.read(); // BANG! 

ve zor yolu öğrenirler.

Burada yalnızca güçlü bir şekilde yazılmış dillerden bahsettiğimizi unutmayın: Güçlü bir şekilde yazılmış bir dilde bir API'nın ne olduğu hakkında net bir fikir var: Her biri için açık bir şekilde tanımlanmış bir protokolle kullanmanız için işlevselliğin (yöntemlerin) bir örneği.

Bu açıkça tanımlanmış protokol tipik olarak bir yöntem imzası ile tanımlanır. Burada fopen, bir dize (veya C durumunda bir char *) iletmenizi gerektirir. Başka bir şey verirseniz, derleme zamanı hatası alırsınız. Protokolü takip etmediniz - API'yi düzgün kullanmıyorsunuz.

Bazı (belirsiz) dillerde dönüş türü de protokolün bir parçasıdır. Eşdeğerini aramaya çalışırsanızfopen()Bazı dillerdeki bir değişkene atamadan derleme zamanı hatası da alırsınız (bunu yalnızca geçersiz işlevlerle yapabilirsiniz).

Yapmaya çalıştığım nokta şudur: Statik olarak yazılan bir dilde API programcısı, herhangi bir bariz hata yaparsa istemci kodlarının derlenmesini engelleyerek istemciyi API'yi doğru şekilde kullanmaya teşvik eder.

(Ruby gibi dinamik olarak yazılan bir dilde, dosya adı olarak herhangi bir şey aktarabilir, kayan nokta söyleyebilirsiniz - ve derlenir. Burada yapılan argümanlar yalnızca statik olarak yazılmış diller için geçerlidir.)

Kontrol edilen istisnalar ne olacak?

İşte bir dosya açmak için kullanabileceğiniz Java API'lerinden biri.

try {
  f = new FileInputStream("goodluckfindingthisfile");
}
catch (FileNotFoundException e) {
  // deal with it. No really, deal with it!
  ... // this is me dealing with it
}

Şu yakalamayı gördün mü? İşte bu API yönteminin imzası:

public FileInputStream(String name)
                throws FileNotFoundException

Not FileNotFoundExceptionbir olduğunu kontrol istisna.

API programcısı size şunu söylüyor: "Bu yapıcıyı yeni bir FileInputStream oluşturmak için kullanabilirsiniz, ancak

a) gerekir String gibi dosya adı geçmektedir
) b gerekir "dosya zamanında bulunamadığı ihtimalini kabul

Ve endişelendiğim kadarıyla bütün mesele bu.

Anahtar temelde sorunun "programcının kontrolü dışında olan şeyler" olarak ifade ettiği şeydir. İlk düşüncem, API dışındaki şeyler anlamına gelmesiydi. programcılarının kontrolü . Ancak, doğru bir şekilde kullanıldığında kontrol edilen istisnalar, hem istemci programcısının hem de API programcısının kontrolünün dışında olan şeyler için gerçekten olmalıdır. Bu, kontrol edilen istisnaları kötüye kullanmamanın anahtarı olduğunu düşünüyorum.

Bence dosya açma bu noktayı güzel bir şekilde gösteriyor. API programcısı, API çağrıldığında varolmayan bir dosya adı verebileceğinizi ve size istediğinizi geri getiremeyeceklerini, ancak bir istisna atmaları gerekeceğini biliyor. Ayrıca, bunun oldukça düzenli olacağını ve istemci programcısının, aramayı yazdıkları sırada dosya adının doğru olmasını bekleyebileceğini biliyorlar, ancak çalışmalarının kontrolünün ötesindeki nedenlerden dolayı yanlış olabilir.

Bu yüzden API bunu açıkça ortaya koyuyor: Beni aradığınızda bu dosyanın mevcut olmadığı ve onunla daha iyi anlaştığınız durumlar olacak.

Bu bir karşı dava ile daha açık olur. Bir tablo API'sı yazdığımı düşünün. Bir yerde bu yöntemi içeren bir API ile tablo modeli var:

public RowData getRowData(int row) 

Şimdi bir API programcısı olarak bazı istemcilerin satır veya tablonun dışında bir satır değeri için negatif bir değer geçirdiği durumlar olacağını biliyorum. Bu yüzden kontrol edilmiş bir istisna atmaya ve müşteri ile başa çıkmak için cazip olabilir:

public RowData getRowData(int row) throws CheckedInvalidRowNumberException

(Gerçekten "Checked" demezdim elbette.)

Bu, kontrol edilen istisnaların kötü kullanımıdır. İstemci kodu, her biri bir dene / yakala kullanmak zorunda kalacak satır verilerini almak için çağrılarla dolu olacak ve ne için? Kullanıcıya yanlış satır arandığını rapor edecekler mi? Muhtemelen hayır - tablo görünümümü çevreleyen kullanıcı arayüzü ne olursa olsun, kullanıcının geçersiz bir satırın istendiği bir duruma girmesine izin vermemelidir. Yani istemci programcısının bir hatası.

API programcısı, istemcinin bu tür hataları kodlayacağını ve bunu bir çalışma zamanı istisnasıyla işlemesi gerektiğini tahmin edebilir IllegalArgumentException.

getRowDataKontrollü bir istisna dışında , bu açıkça Hejlsberg'in tembel programcısına sadece boş yakalamalar ekleyecek bir durumdur. Bu olduğunda, geçersiz satır değerleri test cihazı veya istemci geliştirici hata ayıklaması için bile açık olmayacak, bunun yerine kaynağını belirlemek zor olan takılma hatalarına yol açacaktır. Arianne roketleri fırlatmadan sonra patlayacak.

Tamam, işte sorun: İşaretli istisnanın FileNotFoundExceptionsadece iyi bir şey değil, API programcıları araç kutusunda API'yi istemci programcı için en yararlı şekilde tanımlamak için gerekli bir araç olduğunu söylüyorum . Ancak, CheckedInvalidRowNumberExceptionkötü programlamaya yol açan büyük bir rahatsızlıktır ve kaçınılmalıdır. Ama farkı nasıl söyleyebilirim.

Sanırım bu tam bir bilim değil ve sanırım Hejlsberg'in argümanının altını çiziyor ve belki de belli bir dereceye kadar haklı. Ama bebeği burada banyo suyuyla atmaktan mutlu değilim, bu yüzden iyi kontrol edilmiş istisnaları kötülerden ayırt etmek için buraya bazı kurallar çıkarmama izin verin:

  1. Müşterinin kontrolü dışında veya Kapalı vs Açık:

    İşaretli istisnalar yalnızca hata durumunun hem API'nin hem de istemci programcısının kontrolü dışında olduğu durumlarda kullanılmalıdır . Bu, sistemin ne kadar açık veya kapalı olduğu ile ilgilidir. Bir de kısıtlı müşteri programcı düğmeleri, klavye komutları vs tümü üzerinde, diyelim ki, kontrole sahip UI tablo görünümü (kapalı bir sistem), bir istemci programlama hata olduğunu gelen eklenti ve sil satırlar o veri almaya çalışırsa o var olmayan bir satır. Çok sayıda kullanıcının / uygulamanın dosya ekleyip silebildiği dosya tabanlı bir işletim sisteminde (açık bir sistem), istemcinin istediği dosyanın bilgisi olmadan silinmiş olması mümkündür, bu yüzden onunla başa çıkmaları beklenir. .

  2. Her yerde kullanım:

    İşaretli istisnalar, istemci tarafından sık yapılan bir API çağrısında kullanılmamalıdır. Sık sık, müşteri kodunda birçok yerde kastediyorum - sık sık değil. Bu nedenle, bir istemci kodu aynı dosyayı çok fazla açmaya çalışmaz, ancak tablo görünümüm RowDatafarklı yöntemlerden her yeri alır . Özellikle, bir sürü kod yazacağım

    if (model.getRowData().getCell(0).isEmpty())

    ve her seferinde denemek / yakalamak sarmak acı verici olacaktır.

  3. Kullanıcıyı Bilgilendirme:

    İşaretli istisnalar, son kullanıcıya faydalı bir hata mesajı sunulduğunu hayal edebileceğiniz durumlarda kullanılmalıdır. Bu "ve bu olduğunda ne yapacaksın?" soru ben yukarıda yetiştirdim. Ayrıca madde 1 ile de ilgilidir. İstemci-API sisteminizin dışındaki bir şeyin dosyanın orada olmamasına neden olabileceğini tahmin edebileceğiniz için, kullanıcıya makul bir şekilde bunu anlatabilirsiniz:

    "Error: could not find the file 'goodluckfindingthisfile'"

    Yasadışı satır numaranıza dahili bir hata neden olduğu ve kullanıcının hatasından kaynaklandığı için, onlara verebileceğiniz hiçbir yararlı bilgi yoktur. Uygulamanız çalışma zamanı istisnalarının konsola girmesine izin vermezse, muhtemelen onlara aşağıdaki gibi çirkin bir mesaj verir:

    "Internal error occured: IllegalArgumentException in ...."

    Kısacası, istemci programlayıcınızın istisnanızı kullanıcıya yardımcı olacak bir şekilde açıklayabileceğini düşünmüyorsanız, muhtemelen işaretli bir istisna kullanmamalısınız.

Bunlar benim kurallarım. Biraz uyuştu ve şüphesiz istisnalar olacak (lütfen eğer onları iyileştirmem için bana yardım et). Ancak benim asıl argümam, FileNotFoundExceptionkontrol edilen istisnanın, parametre türleri kadar API sözleşmesinin bir parçası kadar önemli ve yararlı olduğu durumlar olduğu yönündedir. Bu yüzden sadece yanlış kullanıldığından vazgeçmemeliyiz.

Üzgünüm, bunu bu kadar uzun ve waffly yapmak istememiştim. İki öneriyle bitireyim:

Y: API programcıları: kullanışlılıklarını korumak için işaretli istisnaları az miktarda kullanın. Şüphe duyduğunuzda kontrol edilmeyen bir istisna kullanın.

B: Müşteri programcıları: geliştirme sürecinizin başında sarılmış bir istisna (google it) oluşturma alışkanlığı edinin. JDK 1.4 ve üzeri bunun için bir kurucu sağlar RuntimeException, ancak kendiniz de kolayca oluşturabilirsiniz. İşte kurucu:

public RuntimeException(Throwable cause)

Daha sonra kontrol edilen bir istisnayı ele almanız gerektiğinde ve tembel hissettiğinizde (veya API programcısının kontrol edilen istisnayı ilk etapta aşırı derecede hevesli olduğunu düşünüyorsanız) alıştırın, sadece istisnayı yutmayın, sarın ve yeniden düşünün.

try {
  overzealousAPI(thisArgumentWontWork);
}
catch (OverzealousCheckedException exception) {
  throw new RuntimeException(exception);  
}

Bunu IDE'nizin küçük kod şablonlarından birine koyun ve tembel hissettiğinizde kullanın. Bu şekilde, kontrol edilen istisnayı gerçekten ele almanız gerekiyorsa, sorunu çalışma zamanında gördükten sonra geri gelmek ve onunla başa çıkmak zorunda kalacaksınız. Çünkü, inanın bana (ve Anders Hejlsberg), bu TODO'ya asla geri dönmeyeceksiniz.

catch (Exception e) { /* TODO deal with this at some point (yeah right) */}

110
Dosyaları açmak aslında tamamen ters ürün kontrol istisnaları için iyi bir örnektir. Çoğu zaman dosyayı açan kod istisna hakkında yararlı bir şey YAPAMAZ - bu çağrı yığını kadar birkaç katman daha iyi yapılır. İşaretli istisna, yöntem imzalarınızı karmaşaya sokmaya zorlar, böylece zaten ne yapacağınızı yapabilirsiniz - istisnayı en mantıklı olduğu yerde işleyin.
Michael Borgwardt

117
@Michael: Genel olarak bu G / Ç istisnalarını birkaç seviye daha üst düzeyde tutabileceğiniz konusunda hemfikiriz - ancak bir API istemcisi olarak bunları tahmin etmeniz gerektiğini inkar ettiğinizi duymuyorum . Bu nedenle, kontrol edilen istisnaların uygun olduğunu düşünüyorum. Evet, çağrı yığını kadar her yönteme "atar" ilan etmek zorunda kalacaksınız, ama bunun dağınıklık olduğunu kabul etmiyorum. Yöntem imzalarınızdaki yararlı bildirim bilgileri. Diyorsunuz: düşük seviyeli yöntemlerim eksik bir dosyaya girebilir, ancak bunun işlenmesini size bırakacaklardır. Kaybedecek bir şey görmüyorum ve sadece kazanmak için iyi, temiz bir kod görüyorum
Rhubarb

23
@Rhubarb: +1, çok ilginç bir cevap, her iki taraf için de argümanlar gösteriyor. Harika. Son yorumunuz hakkında: özellikle de yol boyunca bir arabirim uyguluyorsanız, çağrı yığınını yukarı çeken her yöntemin atılmasının her zaman mümkün olmayabileceğini unutmayın.
R. Martinho Fernandes

8
Bu çok güçlü bir argüman (ve genel olarak buna katılıyorum), ama işe yaramadığı bir durum var: genel geri aramalar. Bazı kütüphane bazı kullanıcı tanımlı kodu çağırır varsa, o kitaplığı, onu çağıran kullanıcı koduna çağırdığı kullanıcı kodundan denetlenen özel durumlar yaymak için (bildiğim, java olarak) yolu yoktur. Bu sorun ayrıca, uygun genel türleri desteklemeyen birçok statik olarak yazılmış dilin tür sisteminin diğer bölümlerine de uzanır. Bu cevap, bundan bahsedilerek (ve belki de bir yanıtla) geliştirilecektir.
Mankarse

7
Yanlış @Rhubarb. Kontrol edilen istisnalar, ele alınabilecek ve ele alınması gereken 'beklenmedik durumlar' için amaçlanmıştır, ancak her zaman "erken at, geç yakala" en iyi uygulamaları ile uyumlu değildir. Bir dosya açma kiraz aldı örneği, olabilir ele. Çoğu IOException (örn. Bayt yazma), SQLException, RemoteException anlamlı bir şekilde işlemek imkansızdır. Başarısızlık veya yeniden deneme "iş" veya "istek" düzeyinde olmalıdır ve Java kütüphanelerinde kullanıldığı gibi kontrol edilen istisnalar bunu zorlaştıran bir hatadır. literatejava.com/exceptions/…
Thomas W

184

Kontrollü istisnalarla ilgili olan şey, kavramın olağan anlayışı ile gerçekten istisna olmamalarıdır. Bunun yerine, API alternatif dönüş değerleridir.

İstisnaların tüm fikri, çağrı zincirinin aşağısında bir yere atılan bir hatanın, araya giren kodun endişelenmesine gerek kalmadan kabartılabilir ve kodla daha ileri bir yerde ele alınabileceğidir. Öte yandan, kontrol edilen istisnalar, atıcı ve yakalayıcı arasındaki her düzeyde kodun, bunlardan geçebilecek tüm istisna biçimleri hakkında bildiklerini beyan etmelerini gerektirir. Bu, uygulamada, kontrol edilen istisnaların yalnızca arayanın kontrol etmesi gereken özel dönüş değerleri olup olmadığı konusunda çok az farklıdır. örneğin [yalancı kod].:

public [int or IOException] writeToStream(OutputStream stream) {
    [void or IOException] a= stream.write(mybytes);
    if (a instanceof IOException)
        return a;
    return mybytes.length;
}

Java, alternatif dönüş değerleri veya dönüş değerleri olarak basit satır içi tuples yapamadığından, denetlenen özel durumlar makul bir yanıttır.

Sorun, standart kütüphanenin büyük alanları da dahil olmak üzere birçok kodun, birkaç seviyeyi yakalamak isteyebileceğiniz gerçek istisnai durumlar için yanlış kontrol istisnalarını içermesidir. IOException neden bir RuntimeException değil? Diğer her dilde bir G / Ç istisnasının olmasına izin verebilirim ve işlemek için hiçbir şey yapmazsam, uygulamam duracak ve bakmak için kullanışlı bir yığın izlemesi alacağım. Bu olabilecek en iyi şey.

Belki örnekten iki yöntem, akışa yazma işleminin tümünden tüm IOExceptions öğelerini yakalamak, işlemi iptal etmek ve hata raporlama koduna atlamak istiyorsunuz; Java'da bunu her çağrı seviyesinde 'IOException atar' eklemeden yapamazsınız, hatta kendileri IO yapmayan seviyeler bile. Bu tür yöntemlerin istisna yönetimi hakkında bilgi sahibi olması gerekmez; imzalarına istisnalar eklemek zorunda kalmak:

  1. gereksiz yere bağlantıyı artırır;
  2. arayüz imzalarını değiştirmeyi çok kırılgan hale getirir;
  3. kodu daha az okunabilir yapar;
  4. o kadar sinir bozucu ki, ortak programcı tepkisi sistemi 'İstisna atar', 'yakala (İstisna e) {}' gibi korkunç bir şey yaparak veya her şeyi bir RuntimeException'a (hata ayıklamayı zorlaştırır) sararak sistemi yenmektir.

Ve sonra çok saçma kütüphane istisnaları var:

try {
    httpconn.setRequestMethod("POST");
} catch (ProtocolException e) {
    throw new CanNeverHappenException("oh dear!");
}

Kodunuzu böyle gülünç bir kıvrımla karıştırmanız gerektiğinde, gerçekten basit zayıf API tasarımı olmasına rağmen, kontrol edilen istisnaların bir grup nefret alması şaşırtıcı değildir.

Diğer bir kötü etki, A bileşeninin genel B bileşenine geri arama sağladığı Kontrolün Ters Çevirilmesi üzerindedir. A bileşeni, bir istisnanın B bileşenini çağırdığı yere geri çağırmasına izin vermek ister, ancak yapamaz. çünkü bu, B tarafından düzeltilen geri çağırma arayüzünü değiştirir. A, yalnızca bir RuntimeException içinde gerçek istisnayı sararak bunu yapabilir, ki bu da daha fazla istisna kullanan bir kaynak plakasıdır.

Java ve standart kütüphanesinde uygulanan istisnalar, kaynatma levhası, kaynatma levhası, kaynatma levhası anlamına gelir. Zaten ayrıntılı bir dilde bu bir kazanç değildir.


14
Kod örneğinizde, günlükleri okurken orijinal nedenin bulunabilmesi için özel durumları zincirlemek en iyisidir: throw CanNeverHappenException (e);
Esko Luontola

5
Katılmıyorum. İşaretli veya işaretsiz istisnalar istisnai durumlardır. Örnek: HTTP üzerinden bir nesneyi alan bir yöntem. Dönüş değeri başka nesne veya hiçbir şey, kötü gidebilir her şey olağanüstü. Onlara C'de yapıldığı gibi dönüş değerleri olarak davranmak sadece karışıklığa ve zayıf tasarıma yol açar.
Bay Smith

15
@Mister: Dediğim şey, Java'da uygulandığı gibi işaretli istisnaların pratikte, C ++ ve diğer Java öncesi dillerden tanıyabileceğimiz geleneksel 'istisnalar' yerine C'deki gibi dönüş değerleri gibi davranmasıdır. Ve bu IMO aslında karışıklığa ve zayıf tasarıma yol açıyor.
bobince

9
Standart kütüphanelerin kontrol edilen istisnaları kötüye kullanmasının karışıklığa ve kötü yakalama davranışına kesinlikle eklendiğini kabul edin. Ve çoğu zaman sadece zayıf belgelerden, örn. "Bazı diğer G / Ç hatası oluştuğunda" IOException özel durumunu ortaya koyan disconnect () gibi bir parçalama yöntemi. Şey, bağlantıyı kesiyordum! Bir tanıtıcı mı yoksa başka bir kaynak mı sızdırıyorum? Tekrar denemem gerekir mi? Neden olduğunu bilmeden , yapmam gereken eylemi türetemiyorum ve bu yüzden sadece yutmak, yeniden denemek veya kefalet etmek zorunda olup olmadığımı tahmin etmek zorundayım.
charstar

15
"API alternatif dönüş değerleri" için +1. İşaretli istisnalara bakmak için ilginç bir yol.
Zsolt Török

75

İşaretli istisnalara karşı tüm (birçok) nedenleri yeniden ele almak yerine, sadece bir tane seçeceğim. Bu kod bloğunu kaç kez yazdığımın sayısını kaybettim:

try {
  // do stuff
} catch (AnnoyingcheckedException e) {
  throw new RuntimeException(e);
}

% 99'u bu konuda hiçbir şey yapamam. Son olarak bloklar gerekli her türlü temizliği yapar (veya en azından yapmalıdır).

Bunu gördüğüm sayıyı da kaybettim:

try {
  // do stuff
} catch (AnnoyingCheckedException e) {
  // do nothing
}

Neden? Çünkü birisi bununla uğraşmak zorunda kaldı ve tembeldi. Yanlış mıydı? Elbette. Olur mu? Kesinlikle. Ya bunun yerine denetlenmeyen bir istisna olsaydı? Uygulama yeni ölecekti (bir istisnayı yutmak tercih edilir).

Ve sonra bir akış kontrolü biçimi olarak istisnalar kullanan çileden çıkaran bir kodumuz var. java.text.Format yapar. Bzzzt. Yanlış. Formdaki bir sayı alanına "abc" koyarak kullanıcı bir istisna değildir.

Tamam, sanırım bu üç nedendi.


4
Ancak bir istisna düzgün bir şekilde yakalanırsa, kullanıcıyı bilgilendirebilir, diğer görevleri yapabilir (günlük?) Ve uygulamadan kontrollü bir şekilde çıkabilirsiniz. Bazı API bölümlerinin daha iyi tasarlanmış olabileceğini kabul ediyorum. Ve tembel programcı nedeni için, ben bir programcı olarak kodunuz% 100 sorumlu olduğunu düşünüyorum.
Bay Smith

3
try-catch-rethrow'un bir mesaj belirtmenize izin verdiğini unutmayın - bunu genellikle durum değişkenlerinin içeriği hakkında bilgi eklemek için kullanırım. Sık karşılaşılan bir örnek, IOExceptions'ın söz konusu dosyanın absolutePathName () yöntemini eklemesidir.
Thorbjørn Ravn Andersen

15
Eclipse gibi IDE'lerin boş catch bloğunu kaç kez gördüğünüz için suçlanacak çok şey olduğunu düşünüyorum. Gerçekten, varsayılan olarak yeniden düşünmeliler.
artbristol

12
"% 99 bu konuda hiçbir şey yapamıyorum" - yanlış, kullanıcıya yalnızca uygulamanın çökmesine izin vermek yerine "Sunucuya bağlanılamadı" veya "IO cihazı başarısız oldu" şeklinde bir mesaj gösterebilirsiniz. küçük bir ağ hıçkırık nedeniyle. Her iki örnek de kötü programcıların çalışma sanatlarıdır. Kötü programcılara saldırmalı ve istisnaları kendileri kontrol etmemelisiniz. Tıpkı salata sosu olarak kullandığımda diyabetime yardım etmemek için insüline saldırmak gibi.
AxiomaticNexus

2
@YasmaniLlanes Bunları her zaman yapamazsınız. Bazen uymak için bir arayüzünüz olabilir. Ve bu, özellikle iyi korunabilir API'ler tasarladığınızda doğrudur, çünkü her yere yan etkiler atmaya başlayamazsınız. Hem bu hem de getireceği karmaşıklık sizi büyük ölçekte ciddi bir şekilde ısırır. Yani evet, zamanın% 99'u, bu konuda yapılacak bir şey yok.
MasterMastic

50

Bunun eski bir soru olduğunu biliyorum ama kontrol edilmiş istisnalarla güreştim ve ekleyeceğim bir şey var. Lütfen bu süre boyunca beni affet!

Kontrollü istisnaları olan ana sığır etim, polimorfizmi bozmalarıdır. Polimorfik arayüzlerle güzel oynamalarını sağlamak imkansızdır.

İyi ol 'Java al List arayüzünü alın. Biz uygulamaları gibi bellek Common'ı var ArrayListve LinkedList. Ayrıca AbstractListyeni liste türlerini tasarlamayı kolaylaştıran iskelet sınıfımız da var . Salt okunur bir liste için yalnızca iki yöntem uygulamamız gerekir: size()veget(int index) .

Bu örnek WidgetListsınıf Widget, bir dosyadan (gösterilmemiştir) bazı sabit boyutlu nesneleri bir dosyadan okur :

class WidgetList extends AbstractList<Widget> {
    private static final int SIZE_OF_WIDGET = 100;
    private final RandomAccessFile file;

    public WidgetList(RandomAccessFile file) {
        this.file = file;
    }

    @Override
    public int size() {
        return (int)(file.length() / SIZE_OF_WIDGET);
    }

    @Override
    public Widget get(int index) {
        file.seek((long)index * SIZE_OF_WIDGET);
        byte[] data = new byte[SIZE_OF_WIDGET];
        file.read(data);
        return new Widget(data);
    }
}

Tanıdıkları kullanarak Widget'ları göstererek List arabirimi kendisi hakkında bilgi sahibi olmak zorunda kalmadan öğeleri ( list.get(123)) alabilir veya bir listeyi ( for (Widget w : list) ...) yineleyebilirsiniz WidgetList. Bu liste, genel listeler kullanan herhangi bir standart yönteme geçirilebilir veya a Collections.synchronizedList. Bunu kullanan kodun "Widget'ların" yerinde mi oluşturulduğunu, bir diziden gelip gelmediğini veya bir dosyadan ya da veritabanından ya da ağ üzerinden ya da gelecekteki bir alt boşluk geçişinden okunup okunmadığını bilmesine ya da umursamasına gerek yoktur. ListArabirim doğru bir şekilde uygulandığından hala düzgün çalışacaktır .

Dışında değil. Yukarıdaki sınıf derlenmez çünkü dosya erişim yöntemleri IOException"yakalamanız veya belirtmeniz" gereken bir işaretli istisna atabilir . Sen o kadar atılmış belirleyemezsiniz - o sözleşmesini ihlal edeceği çünkü derleyici izin vermezListarayüzüne. VeWidgetListkendisinin istisnayı elealmasının yararlı bir yolu yoktur(daha sonra açıklayacağım gibi).

Görünüşe göre yapılacak tek şey, denetlenmeyen istisnaları denetlenmeyen bir istisna olarak yakalamak ve yeniden değerlendirmektir:

@Override
public int size() {
    try {
        return (int)(file.length() / SIZE_OF_WIDGET);
    } catch (IOException e) {
        throw new WidgetListException(e);
    }
}

public static class WidgetListException extends RuntimeException {
    public WidgetListException(Throwable cause) {
        super(cause);
    }
}

((Düzenle: Java 8 bir UncheckedIOException tam olarak bu durum için sınıf : IOExceptionpolimorfik yöntem sınırlarını aşmak ve yeniden sorgulamak için .

Bu nedenle, kontrol edilen istisnalar böyle durumlarda işe yaramaz . Onları atamazsın. MapBir veritabanı tarafından desteklenen bir akıllı için Ditto veyajava.util.Random bir COM portu aracılığıyla bir kuantum entropi kaynağına bağlı . Bir polimorfik arayüzün uygulanmasıyla yeni bir şey yapmaya çalıştığınızda, kontrol edilen istisnalar kavramı başarısız olur. Ancak kontrol edilen istisnalar o kadar sinsidir ki sizi hala huzur içinde bırakmazlar, çünkü hala alt düzey yöntemlerden herhangi birini yakalamanız ve geri almanız, kodu karıştırmanız ve yığın izlemesini karıştırmanız gerekir.

RunnableKontrol edilen istisnaları atan bir şey çağırırsa , her yerde bulunan arayüzün genellikle bu köşeye geri döndüğünü görüyorum . İstisnayı olduğu gibi atamaz, bu yüzden tek yapabileceği kodu yakalayıp yeniden atarak kodun dağınıklığıdır RuntimeException.

Aslında yapabilirsiniz sen kesmek başvurmak durumunda istisnalar kontrol bildirilmemiş atmak. JVM, çalışma zamanında, kontrol edilen istisna kurallarını umursamıyor, bu yüzden sadece derleyiciyi kandırmalıyız. Bunu yapmanın en kolay yolu jenerik ilaçları kötüye kullanmaktır. Bu benim yöntemimdir (sınıf adı gösterilmektedir, çünkü (Java 8'den önce) genel yöntem için çağrı sözdiziminde gereklidir):

class Util {
    /**
     * Throws any {@link Throwable} without needing to declare it in the
     * method's {@code throws} clause.
     * 
     * <p>When calling, it is suggested to prepend this method by the
     * {@code throw} keyword. This tells the compiler about the control flow,
     * about reachable and unreachable code. (For example, you don't need to
     * specify a method return value when throwing an exception.) To support
     * this, this method has a return type of {@link RuntimeException},
     * although it never returns anything.
     * 
     * @param t the {@code Throwable} to throw
     * @return nothing; this method never returns normally
     * @throws Throwable that was provided to the method
     * @throws NullPointerException if {@code t} is {@code null}
     */
    public static RuntimeException sneakyThrow(Throwable t) {
        return Util.<RuntimeException>sneakyThrow1(t);
    }

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

Yaşa! Bunu kullanarak, yığının herhangi bir derinliğini bildirmeksizin, RuntimeExceptiona'ya sarmadan ve yığın izlemesini karıştırmadan herhangi bir derinliği atabiliriz ! "WidgetList" örneğini tekrar kullanma:

@Override
public int size() {
    try {
        return (int)(file.length() / SIZE_OF_WIDGET);
    } catch (IOException e) {
        throw sneakyThrow(e);
    }
}

Ne yazık ki, kontrol edilen istisnaların son hakareti, derleyicinin , kusurlu görüşüne göre atılamazsa, kontrol edilen bir istisnayı yakalamanızı reddetmeyi reddetmesidir . (İşaretlenmeyen istisnaların bu kuralı yoktur.) Gizlice atılan istisnayı yakalamak için bunu yapmalıyız:

try {
    ...
} catch (Throwable t) { // catch everything
    if (t instanceof IOException) {
        // handle it
        ...
    } else {
        // didn't want to catch this one; let it go
        throw t;
    }
}

Bu biraz garip, ama artı tarafta, bir sarılmış kontrol edilmiş bir istisna ayıklama kodu hala biraz daha basittir RuntimeException.

Ne mutlu ki, buradaki throw t;açıklama türüt Java 7'de yakalama istisnaları hakkında yeniden düzenleme ile ilgili bir kural sayesinde kontrol edilmesine yasaldır.


İşaretli istisnalar polimorfizmi karşıladığında, bunun tersi de bir sorundur: bir yöntemin potansiyel olarak denetlenen bir istisnayı attığı belirtildiğinde, ancak geçersiz kılınan bir uygulama gerçekleşmez. Örneğin, soyut sınıf OutputStream's writeyöntemleri tüm belirtin throws IOException.ByteArrayOutputStreamgerçek bir G / Ç kaynağı yerine bellek içi bir diziye yazan bir alt sınıftır. Geçersiz kılınan writeyöntemleri IOExceptions'ye neden olamaz , bu nedenle throwsyan tümceleri yoktur ve bunları yakala veya belirt gereksinimi hakkında endişelenmeden arayabilirsiniz.

Her zaman değil. WidgetBunun bir akışa kaydetmek için bir yöntemi olduğunu varsayalım :

public void writeTo(OutputStream out) throws IOException;

Bu yöntemi bir ova kabul etmek için beyan etmek OutputStream edilmesi doğru olan şeydir, bu nedenle her türlü çıktıyla polimorfik olarak kullanılabilir: dosyalar, veritabanları, ağ vb. Ve bellek içi diziler. Bununla birlikte, bellek içi bir dizide, gerçekte gerçekleşemeyen bir istisnayı işlemek için sahte bir gereksinim vardır:

ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
    someWidget.writeTo(out);
} catch (IOException e) {
    // can't happen (although we shouldn't ignore it if it does)
    throw new RuntimeException(e);
}

Her zamanki gibi, kontrol edilen istisnalar engel olur. Değişkenleriniz daha açık uçlu istisna gereksinimleri olan bir temel tür olarak bildirilirse, bu istisnalar için işleyiciler eklemeniz gerekir. , uygulamanızda gerçekleşmeyeceklerini bilseniz gerekir.

Ama bekleyin, kontrol edilen istisnalar aslında çok can sıkıcı, tersini yapmanıza bile izin vermiyorlar! Şu anda IOExceptiontarafından atılanları yakaladığınızı hayal edinwrite aramalarlaOutputStream , ancak değişkenin bildirilen türünü bir olarak değiştirmek istediğinizde ByteArrayOutputStream, derleyici atılamadığını söyleyen kontrol edilen bir istisnayı yakalamaya çalıştığınız için sizi oylayacaktır.

Bu kural bazı saçma problemlere neden olur. Örneğin, üç tek writeyöntemlerinde OutputStreamolduğu değil tarafından geçersiz ByteArrayOutputStream. Özellikle, 0 ofseti ve dizinin uzunluğu ile write(byte[] data)çağırarak tam diziyi yazan bir kolaylık yöntemidir write(byte[] data, int offset, int length). ByteArrayOutputStreamüç bağımsız değişken yöntemini geçersiz kılar, ancak tek bağımsız değişken kolaylık yöntemini olduğu gibi devralır. Devralınan yöntem tam olarak doğru olanı yapar, ancak istenmeyen bir throwscümle içerir . Bu belki de tasarımında bir gözetimdi.ByteArrayOutputStream , ama asla düzeltemezler çünkü istisna yakalayan herhangi bir kodla kaynak uyumluluğunu kıracaktı - asla olmayan, asla ve asla atılan istisna!

Bu kural düzenleme ve hata ayıklama sırasında da can sıkıcıdır. Örneğin, bazen bir yöntem çağrısını geçici olarak yorumlayacağım ve eğer kontrol edilmiş bir istisna atarsa, derleyici şimdi yerel tryve catchblokların varlığından şikayet edecek . Bu yüzden ben de bu yorum yapmak zorunda ve şimdi içinde kodu düzenlerken, IDE yanlış seviyeye girintili çünkü {ve }yorumlanır. Gah! Bu küçük bir şikayet ama kontrol istisnaları şimdiye kadar yapmak tek şey sorun neden gibi görünüyor.


Neredeyse bitti. Kontrol edilmiş istisnalar dışında son hayal kırıklığım , çoğu çağrı sitesinde , onlarla yapabileceğiniz yararlı bir şey olmamasıdır. İdeal olarak bir şeyler ters gittiğinde kullanıcıyı sorun hakkında bilgilendirebilecek ve / veya işlemi uygun şekilde sonlandırabilecek veya yeniden deneyebilecek, uygulamaya özel, yetkin bir işleyicimiz olacaktır. Bunu yalnızca yığının yukarısındaki bir işleyici yapabilir, çünkü genel hedefi bilen tek kişi budur.

Bunun yerine, derleyiciyi kapatmanın bir yolu olarak yaygın olan aşağıdaki deyimi alıyoruz:

try {
    ...
} catch (SomeStupidExceptionOmgWhoCares e) {
    e.printStackTrace();
}

Bir GUI veya otomatik programda yazdırılan mesaj görülmez. Daha da kötüsü, istisnadan sonra kodun geri kalanı ile sürülür. İstisna aslında bir hata değil mi? O zaman yazdırmayın. Aksi takdirde, bir anda başka bir şey patlayacak, bu sırada orijinal istisna nesnesi gidecek. Bu deyim BASIC On Error Resume Nextveya PHP'den daha iyi değildir error_reporting(0);.

Bir çeşit logger sınıfını aramak daha iyi değildir:

try {
    ...
} catch (SomethingWeird e) {
    logger.log(e);
}

Bu kadar tembel e.printStackTrace();ve hala belirsiz bir durumda kodla sürüyor. Ayrıca, belirli bir günlük sisteminin veya başka bir işleyicinin seçimi uygulamaya özgüdür, bu nedenle kodun yeniden kullanımı zarar görür.

Fakat bekle! Uygulamaya özel işleyiciyi bulmanın kolay ve evrensel bir yolu vardır. Çağrı yığınının üstündedir (veya İş Parçacığının yakalanmayan istisna işleyicisi olarak ayarlanır ). Çoğu yerde, tek yapmanız gereken istisnayı yığının üstüne çıkarmak . Ör throw e;. İşaretli istisnalar sadece engel olur.

Eminim dil kontrol edildiğinde kontrol edilen istisnalar iyi bir fikir gibi geldi, ama pratikte onları rahatsız edici ve faydasız buldum.


WidgetList ile boyut yönteminiz için, boyutu bir değişkende önbelleğe alıp yapıcıda ayarlayacağım. Yapıcı bir istisna atmakta özgürdür. WidgetList'i kullanırken dosya değişirse bu çalışmaz, ki bu muhtemelen kötü olurdu.
2013'te

2
SomeStupidExceptionOmgWhoCares iyi birisi onu atmak için yeterince önemsedi. Yani ya asla atılmamalıydı (kötü tasarım) ya da gerçekten idare etmelisin. Aynı şey, tasarımın maalesef kötü olduğu 1.0 öncesi bir sınıfın (bayt dizi çıkış akışı) kötü uygulanması için de geçerlidir.
TofuBeer

2
Uygun deyim, iç içe geçmiş rutin çağrılar tarafından atılan belirtilen istisnaları yakalayacak ve bunları a RuntimeException. Bir rutinin eşzamanlı olarak bildirilebileceğini throws IOExceptionve yine de IOExceptioniç içe bir çağrıdan atılan herhangi birinin beklenmedik ve sarılmış olarak kabul edilmesi gerektiğini belirtebileceğini unutmayın .
supercat

15
Bu yazıda tökezleyen bazı Java deneyimi olan profesyonel bir C # geliştiricisiyim. Neden herkesin bu tuhaf davranışı destekleyeceğine şaşkınım. .NET'te belirli bir özel durum türü yakalamak istiyorsam, bunu yakalayabilirim. Yığının atılmasına izin vermek istersem yapacak bir şey yok. Keşke Java o kadar tuhaf olmasaydı. :)
aikeru

"Bazen geçici olarak bir yöntem çağrısını yorumlayacağım" konusunda - Bunun için kullanmayı öğrendim if (false). Atma maddesi problemini önler ve uyarı daha hızlı geri gitmeme yardımcı olur. +++ Bu, yazdığınız her şeye katılıyorum dedi. İşaretli istisnaların bir değeri vardır, ancak bu değer maliyetlerine kıyasla önemsizdir. Neredeyse her zaman yoluna giriyorlar.
maaartinus

45

Bu, bir yığın izlemesi görüntülemek veya sessizce çökmekle ilgili değil. Katmanlar arasındaki hataları iletebilmekle ilgilidir.

Kontrollü istisnalarla ilgili sorun, insanları önemli ayrıntıları (istisna sınıfı) yutmaya teşvik etmeleridir. Bu ayrıntıyı yutmamayı seçerseniz, tüm uygulamanıza atış bildirimleri eklemeye devam etmelisiniz. Bu, 1) yeni bir istisna türünün çok sayıda işlev imzasını etkileyeceği ve 2) gerçekten yakalamak istediğiniz istisnanın belirli bir örneğini kaçırabileceğiniz anlamına gelir (örneğin, bir İkincil dosya isteğe bağlıdır, bu nedenle hatalarını göz ardı edebilirsiniz, ancak imza nedeniyle throws IOExceptionbunu atlamak kolaydır).

Aslında bir uygulamada bu durumla ilgileniyorum. Neredeyse istisnaları AppSpecificException olarak yeniden paketledik. Bu imzaları gerçekten temiz kıldı ve patlamaktan endişe etmedikthrows .

Tabii ki, şimdi yeniden işleme mantığı ve benzerlerini uygulayarak hata işlemeyi daha yüksek seviyelerde uzmanlaştırmamız gerekiyor. Her şey AppSpecificException olsa da "IOException atılırsa, yeniden deneyin" veya "ClassNotFound atılırsa, tamamen iptal" diyemeyiz. Gerçek istisnayı elde etmek için güvenilir bir yolumuz yoktur, çünkü işler kodumuzla üçüncü taraf kodu arasında geçtikçe tekrar tekrar paketlenir.

Bu yüzden python'da istisna işlemenin büyük bir hayranıyım. Sadece istediğiniz ve / veya ele alabileceğiniz şeyleri yakalayabilirsiniz. Her şey sanki kendiniz yeniden (sanki zaten yapmışsınız) sanki kabarıyor.

Tekrar tekrar buldum ve bahsettiğim proje boyunca, istisna işleme 3 kategoriye ayrılıyor:

  1. Belirli bir istisnayı yakalayın ve yönetin . Bu, örneğin yeniden deneme mantığını uygulamak içindir.
  2. Diğer istisnaları yakalayın ve yeniden düşünün . Burada olan her şey genellikle günlük kaydıdır ve genellikle "$ dosyaadı açılamıyor" gibi bir trite mesajıdır. Bunlar hakkında hiçbir şey yapamayacağınız hatalardır; sadece daha yüksek seviyeler bunun üstesinden gelebilecek kadar iyi bilir.
  3. Her şeyi yakalayın ve bir hata mesajı görüntüleyin. Bu genellikle bir dağıtım programının kökündedir ve tüm yaptığı, hatayı bir İstisna olmayan mekanizma (açılan diyalog, bir RPC Hata nesnesini vb.

5
Düz yöntem imzalarını tutarken ayırmaya izin vermek için belirli AppSpecificException alt sınıflarını yapmış olabilirsiniz.
Thorbjørn Ravn Andersen

1
Ayrıca madde 2 için çok önemli bir ek, yakalanan istisna (örneğin bir RuntimeException içine yerleştirerek) BİLGİ EKLEMEK sağlar olmasıdır. Yığın izinde bulunmayan dosyanın adının bir günlük dosyasında derin gizlenmekten çok, çok daha iyidir.
Thorbjørn Ravn Andersen

1
Temel olarak argümanınız "İstisnaları yönetmek yorucudur, bu yüzden onunla ilgilenmemeyi tercih ederim". İstisna büyüdükçe anlam kaybeder ve bağlam oluşturma pratikte işe yaramaz. Yapmanız gereken bir API tasarımcısı olarak, işlerim yanlış gittiğinde ne beklenebileceğine dair sözleşmeli olarak açıktır, eğer programım çökerse, bu ya da bu istisnanın tasarımcı olarak başarısız olduğu ve başarısızlığınızın bir sonucu olarak sistemim olabildiğince kararlı değil.
Newtopian

4
Söylediğim bu değil. Son cümleniz aslında benimle aynı fikirde. Her şey AppSpecificException içine sarılırsa, kabarmaz (ve anlam / içerik kaybolur) ve evet, API istemcisi bilgilendirilmez - bu, kontrol edilen istisnalarla tam olarak ne olur (java'da olduğu gibi) çünkü insanlar çok fazla throwsbeyan içeren işlevlerle uğraşmak istemiyorlar .
Richard Levasseur

6
@Newtopian - istisnalar büyük ölçüde yalnızca "iş" veya "talep" düzeyinde ele alınabilir. Her küçük potansiyel başarısızlık için değil, büyük ayrıntı düzeyinde başarısız olmak veya yeniden denemek mantıklıdır. Bu nedenle, istisna işleme en iyi uygulama "erken at, geç yakala" olarak özetlenir. İşaretli istisnalar güvenilirliği doğru seviyede yönetmeyi zorlaştırır ve çok sayıda yanlış kodlanmış yakalama bloğunu teşvik eder. literatejava.com/exceptions/…
Thomas W

24

SNR

İlk olarak, kontrol edilen istisnalar kodun "sinyal / gürültü oranını" azaltır. Anders Hejlsberg de benzer bir kavram olan zorunlu ve bildirimsel programlama hakkında konuşuyor. Her neyse, aşağıdaki kod snippet'lerini göz önünde bulundurun:

Java'da UI olmayan iş parçacığından kullanıcı arayüzünü güncelleme:

try {  
    // Run the update code on the Swing thread  
    SwingUtilities.invokeAndWait(() -> {  
        try {
            // Update UI value from the file system data  
            FileUtility f = new FileUtility();  
            uiComponent.setValue(f.readSomething());
        } catch (IOException e) {  
            throw new UncheckedIOException(e);
        }
    });
} catch (InterruptedException ex) {  
    throw new IllegalStateException("Interrupted updating UI", ex);  
} catch (InvocationTargetException ex) {
    throw new IllegalStateException("Invocation target exception updating UI", ex);
}

C # 'da UI olmayan iş parçacığından kullanıcı arayüzünü güncelle:

private void UpdateValue()  
{  
   // Ensure the update happens on the UI thread  
   if (InvokeRequired)  
   {  
       Invoke(new MethodInvoker(UpdateValue));  
   }  
   else  
   {  
       // Update UI value from the file system data  
       FileUtility f = new FileUtility();  
       uiComponent.Value = f.ReadSomething();  
   }  
}  

Bu bana çok daha açık görünüyor. Swing kontrolünde istisnalar gittikçe daha fazla kullanıcı arayüzü çalışması yapmaya başladığınızda gerçekten sinir bozucu ve işe yaramaz hale geliyor.

Hapis Arası

Java'nın Liste arayüzü gibi en temel uygulamaları bile uygulamak için, sözleşmeye göre tasarım aracı olarak kontrol edilen istisnalar düştü. Bir veritabanı veya dosya sistemi veya işaretli bir istisna atan başka bir uygulama tarafından desteklenen bir liste düşünün. Mümkün olan tek uygulama, denetlenen istisnayı yakalamak ve denetlenmeyen bir istisna olarak yeniden oluşturmaktır:

@Override
public void clear()  
{  
   try  
   {  
       backingImplementation.clear();  
   }  
   catch (CheckedBackingImplException ex)  
   {  
       throw new IllegalStateException("Error clearing underlying list.", ex);  
   }  
}  

Ve şimdi tüm bu kodun amacının ne olduğunu sormalısınız. Kontrol edilen istisnalar sadece gürültü ekler, istisna yakalanır ancak ele alınmaz ve sözleşme ile tasarım (kontrol edilen istisnalar açısından) bozulur.

Sonuç

  • İstisnaları yakalamak bunları ele almaktan farklıdır.
  • İşaretli istisnalar koda gürültü ekler.
  • İstisna işleme onlar olmadan C # iyi çalışır.

Bu konuda daha önce blog yazmıştım .


3
Örnekte, hem Java hem de C #, istisnaların işlemeden yayılmasına izin veriyor (Java, IllegalStateException üzerinden). Aradaki fark, bir FileNotFoundException öğesini işlemek isteyebilmenizdir, ancak InvocationTargetException veya InterruptedException öğesinin işlenmesinin yararlı olması olası değildir.
Luke Quinane

3
Ve C # şekilde I / O istisnasının meydana gelebileceğini nasıl bilebilirim? Ayrıca asla koşmaktan bir istisna atmazdım ... Ben istisna işleme kötüye düşünüyorum. Üzgünüz ama kodunuzun bu kısmı için henüz tarafınızı görebiliyorum.
TofuBeer

7
Oraya ulaşıyoruz :-) Yani bir API'nin her yeni sürümünde tüm çağrılarınızı taramak ve olabilecek yeni istisnaları aramak zorunda mısınız? Geriye dönük uyumluluk konusunda endişelenmeleri gerekmediği için şirket API'larında dahili olarak kolayca olabilir.
TofuBeer

3
Şunu mu demek istediniz azaltmak sinyal-gürültü oranı?
neo2862

3
@TofuBeer Underlaying API'sinin arayüzü iyi bir şey değiştikten sonra kodunuzu güncellemek zorunda değil misiniz? Orada yalnızca denetlenmeyen istisnalar olsaydı, bilmeden bozuk / eksik bir programla sonuçlanırdınız.
Francois Bourgeois

23

Artima , .NET mimarlarından biri olan Anders Hejlsberg ile, kontrol edilen istisnalara karşı tartışmaları akut olarak kapsayan bir röportaj yayınladı . Kısa bir çeşnicibaşı:

En azından Java'da uygulandığı şekliyle, atış cümlesi sizi istisnaları ele almaya zorlamaz, ancak bunları ele almazsanız, sizi hangi istisnaların geçebileceğini kesin olarak kabul etmeye zorlar. Bildirilen istisnaları yakalamanızı veya bunları kendi atış cümlelerinize koymanızı gerektirir. Bu gereksinimi çözmek için insanlar saçma şeyler yapar. Örneğin, her yöntemi "İstisna atar" ile süslüyorlar. Bu sadece özelliği tamamen yener ve programcıya daha fazla gobbledy gunk yazmasını sağladınız. Bu kimseye yardım etmez.


2
Aslında baş mimar.
Tuzak

18
Bunu okudum, bana göre argümanı "orada kötü programcılar var" diye kaygılanıyor.
TofuBeer

6
TofuBeer, hiç de değil. Asıl mesele, çağrılan yöntemin atıldığı istisna ile birçok kez ne yapacağınızı bilmemeniz ve gerçekten ilgilendiğiniz durumdan bile söz edilmiyor. Bir dosya açarsınız, bir IO İstisnası alırsınız, örneğin ... bu benim sorunum değil, bu yüzden kusarım. Ancak üst düzey arama yöntemi, yalnızca işlemeyi durdurmak ve kullanıcıya bilinmeyen bir sorun olduğunu bildirmek isteyecektir. İşaretli İstisna hiç yardımcı olmadı. Olabilecek bir milyon tuhaf şeyden biriydi.
Dan Rosenstark

4
@yar, işaretli istisnayı beğenmezseniz, "yeni bir RuntimeException atın (" Foo.bar () ", e) yaparken bunu beklemiyorduk" ve onunla birlikte yapılıyor).
Thorbjørn Ravn Andersen

4
@ ThorbjørnRavnAndersen: Java'da .net maalesef kopyalanan temel bir tasarım zayıflığı, hem bir istisna türünü hem eylemde bulunulması gerekip gerekmediğine karar vermek için birincil araç olarak hem de genel şey türünü belirtmenin birincil aracı olarak kullanmasıdır. aslında iki sorun büyük ölçüde dik olduğunda yanlış gitti. Önemli olan yanlış giden şey değil, hangi durum nesnelerinin içinde olduğu. Dahası, hem .net hem de Java varsayılan olarak bir istisna üzerinde hareket etmenin ve çözmenin genellikle aynı şey olduğunu, aslında genellikle farklı olduklarını varsayarlar.
supercat

20

Başlangıçta sizinle anlaştım, çünkü her zaman kontrol edilmiş istisnaları destekliyorum ve .Net'te istisnaları kontrol etmekten neden hoşlanmadığımı düşünmeye başladım. Ama sonra kontrol edilen istisnalar gibi bulaşmadığımı fark ettim.

Sorunuzu cevaplamak için, evet, programlarımın yığın izlerini, tercihen gerçekten çirkin izlerini göstermesini seviyorum. Uygulamanın hiç görmek isteyebileceğiniz en çirkin hata mesajlarının korkunç bir yığınına patlamasını istiyorum.

Bunun nedeni, eğer bunu yaparsa, düzeltmem gerekiyor ve hemen düzeltmem gerekiyor. Bir sorun olduğunu hemen bilmek istiyorum.

İstisnaları kaç kez ele alıyorsunuz? İstisnaları yakalamaktan bahsetmiyorum - onları idare etmekten mi bahsediyorum? Aşağıdakileri yazmak çok kolay:

try {
  thirdPartyMethod();
} catch(TPException e) {
  // this should never happen
}

Ve bunun kötü bir uygulama olduğunu söyleyebileceğinizi biliyorum, 'cevap' istisna ile bir şeyler yapmak (tahmin edeyim, oturum açmama izin verin), ama Gerçek Dünya'da (tm) çoğu programcı sadece o.

Bu yüzden evet, bunu yapmak zorunda değilsem istisnaları yakalamak istemiyorum ve berbat ettiğimde programımın muhteşem bir şekilde patlamasını istiyorum. Sessizce başarısız olmak olası en kötü sonuçtur.


Java sizi bu tür bir şey yapmaya teşvik eder, böylece her yöntem imzasına her tür özel durum eklemeniz gerekmez.
yfeldblum

17
Komik .. beri benim istisnaları doğru benimsedi ve uygun şekilde kullandım beri benim programları yüzünüzde müşteri memnuniyetsizliği büyük buharda yığını kadar üfleme durdu. Gelişirken büyük çirkin kötü yığın iziniz varsa, müşteri bunları da almak zorundadır. D'YVE, XYZ düğmesi için renk yapılandırmasının ayrıştırılamadığını, bu nedenle varsayılan olarak yazılımın mutlu bir şekilde uğultu ile kullanıldığını söyleyen küçük bir tepsi bildirimi yerine çökmüş sisteminde bir mil yüksekliğinde yığın iziyle ArrayIndexOutOfBoundsException'ı gördüğünde yüzünü görmek için boyunca
Newtopian

1
Belki de Java'nın ihtiyacı olan şey, bir yöntem veya try / catch kod bloğunun içinde meydana gelen belirli bir istisnayı işlemek için hazırlanmadığını ve açık bir yoldan başka yollarla gerçekleşen böyle bir istisna olduğunu belirten bir "cantHandle" bildirisidir. bu yöntem içine atmak (çağrılan bir yöntemin aksine) otomatik olarak bir RuntimeException içinde sarılmalı ve yeniden oluşturulmalıdır. IMHO, kontrol edilen istisnalar , çağrı yığınını sarılmadan nadiren yaymalıdır.
supercat

3
@Newtopian - Sunucu ve yüksek güvenilirlik yazılımı yazıyorum ve bunu 25 yıldır yapıyorum. Programlarım hiç patlamadı ve yüksek kullanılabilirlik, yeniden deneme ve yeniden bağlantı, entegrasyon tabanlı finansal ve askeri sistemlerle çalışıyorum. Çalışma zamanı istisnalarını tercih etmek için mutlak bir hedefim var. İşaretli istisnalar, doğru "erken at, geç yakala" en iyi uygulamayı takip etmeyi zorlaştırır. Doğru güvenilirlik ve hata yönetimi "iş", "bağlantı" veya "talep" düzeyindedir. (Veya bazen verileri ayrıştırırken). İşaretli istisnalar doğru şekilde yapılmasını engeller.
Thomas W

1
Burada bahsettiğiniz istisnalar, RuntimeExceptionsaslında yakalamanıza gerek yok ve programın patlamasına izin vermeniz gerektiğini kabul ediyorum. Her zaman yakalamanız ve işlemeniz gereken istisnalar, denetlenen istisnalardır IOException. Bir alırsanız IOException, kodunuzda düzeltilecek bir şey yoktur; programınız sadece bir ağ hıçkırımı olduğu için patlamamalıdır.
AxiomaticNexus

20

Etkili Java Özel Durumları makalesi , denetlenmeyen istisnaların ne zaman ve ne zaman kullanılacağını kontrol eder. İşte ana noktaları vurgulamak için bu makaleden bazı alıntılar:

Beklenmedik durum: Yöntemin amaçlanan amacına göre ifade edilebilecek bir yöntemden alternatif bir yanıt talep eden beklenen bir durum. Yöntemin arayanı bu tür koşulları bekler ve bunlarla başa çıkma stratejisi vardır.

Hata: Bir yöntemin amaçlanan amacına ulaşmasını engelleyen, yöntemin dahili uygulamasına başvurmadan tanımlanamayan planlanmamış bir durum.

(SO tablolara izin vermez, bu nedenle orijinal sayfadan aşağıdakileri okumak isteyebilirsiniz ...)

olasılık

  • Olarak kabul edilir: Tasarımın bir parçası
  • Gerçekleşmesi bekleniyor: Düzenli fakat nadiren
  • Kimin umurunda: Yöntemi çağıran yukarı akım kodu
  • Örnekler: Alternatif dönüş modları
  • En İyi Eşleme: İşaretli bir istisna

Arıza

  • Şu kabul edilir: Kötü bir sürpriz
  • Gerçekleşmesi bekleniyor: Asla
  • Kimin umurunda: Sorunu çözmesi gereken insanlar
  • Örnekler: Programlama hataları, donanım arızaları, yapılandırma hataları, eksik dosyalar, mevcut olmayan sunucular
  • En İyi Harita Oluşturma: Kontrol edilmeyen bir istisna

Onları ne zaman kullanacağımı biliyorum, neden bu tavsiyeye uymayan insanların ... bu tavsiyelere uymadığını bilmek istiyorum :-)
TofuBeer

Programlama hataları nelerdir ve kullanım hatalarından nasıl ayırt edilir ? Kullanıcı programa yanlış argümanlar iletirse bir programlama hatası mıdır? Java açısından programlama hatası olmayabilir, ancak kabuk komut dosyası bakış açısından bir programlama hatasıdır. Peki geçersiz argümanlar args[]nelerdir? Bir Acil veya Arıza mı?
ceving

3
@TofuBeer - Java kitaplığı tasarımcıları, açıkça işaretlenmemiş olmaları durumunda, kontrol edilemeyen istisnalar olarak her türlü kurtarılamaz düşük düzeyli başarısızlığı koymayı seçti . FileNotFound, örneğin kontrol edilmesi gereken tek IOException'dır. JDBC ile ilgili olarak - sadece veritabanına bağlanma, makul bir olasılık olarak kabul edilebilir . Diğer tüm SQLExceptions bir başarısızlık olmalı ve işaretlenmemiş olmalıdır. Hata işleme "iş" veya "istek" düzeyinde olmalıdır - "erken at, geç yakala" en iyi uygulamalarına bakın. İşaretli istisnalar bunun önünde bir engeldir.
Thomas W

1
Argümanınızda tek bir büyük kusur var. "Beklenmedik durum" istisnalar dışında DEĞİL, iş kodu ve yöntem döndürme değerleri ile ele alınmalıdır. İstisnalar, kelimenin dediği gibi, İSTİSNAL durumlar, bu nedenle Arızalar içindir.
Matteo Mosca

@MatteoMosca Hata dönüş kodları yoksayılma eğilimindedir ve bu onları diskalifiye etmek için yeterlidir. Aslında, olağandışı herhangi bir şey çoğu zaman sadece yığın içinde bir yerde ele alınabilir ve bu istisnalar için bir kullanım durumudur. Ben böyle bir şey düşünebiliriz File#openInputStreamdönen Either<InputStream, Problem>- kastettiğin buysa, o zaman kabul edebilir.
maaartinus

19

Kısacası:

İstisnalar bir API tasarım sorusudur. -- Ne fazla ne az.

İşaretli istisnalar için argüman:

Kontrol edilen istisnaların neden iyi bir şey olmadığını anlamak için, soruyu tersine çevirelim ve soralım: Kontrol edilen istisnalar ne zaman veya neden caziptir, yani derleyicinin istisnaların beyanını zorlamasını istersiniz?

Cevap açıktır: Bazen bir istisna yakalamanız gerekir ve bu sadece çağrılan kod ilgilendiğiniz hata için belirli bir istisna sınıfı sunuyorsa mümkündür.

Dolayısıyla, argüman için kontrol istisnalar derleyici kuvvetleri programcılar istisnalar atılan hangi beyan ve olmasıdır umarım programcı olacak o zaman belge özgü istisna sınıfları ve bunlara neden olan hatalar da.

Gerçekte, çoğu zaman, bir paket com.acmesadece AcmeExceptionbelirli alt sınıfları değil, sadece bir alt sınıfı atar . Arayanların daha sonra işlemesi, bildirmesi veya yeniden sinyal vermesi gerekir AcmeExceptions, ancak bunun olup olmadığından AcmeFileNotFoundErrorveyaAcmePermissionDeniedError .

Bu nedenle, yalnızca AcmeFileNotFoundErrorbiriyle ilgileniyorsanız , çözüm ACME programcılarına bir özellik isteği göndermek ve onlara bu alt sınıfı uygulamalarını, bildirmelerini ve belgelemelerini söylemektir AcmeException.

Ne gereği var?

Bu nedenle, kontrol edilen istisnalar dışında bile, derleyici programcıları yararlı atmaya zorlayamaz istisnalar . Hala API'nin kalitesi ile ilgili bir soru.

Sonuç olarak, kontrol edilmiş istisnaları olmayan diller genellikle çok daha kötü değildir. Programcılar, bir genel Errorsınıftan ziyade genel bir sınıfın spesifik olmayan örneklerini atmaya cazip gelebilir AcmeException, ancak API kalitelerini önemsiyorlarsa,AcmeFileNotFoundError .

Genel olarak, istisnaların özellikleri ve dokümantasyonu, örneğin sıradan yöntemlerin özellikleri ve dokümantasyonundan çok farklı değildir. Bunlar da bir API tasarım sorusudur ve bir programcı yararlı bir özelliği uygulamayı veya dışa aktarmayı unutursa, API ile birlikte çalışabilmeniz için API'nin geliştirilmesi gerekir.

Bu akıl yürütme çizgisini izlerseniz, Java gibi dillerde çok yaygın olan istisnaları bildirme, yakalama ve yeniden atma "güçlüğü" genellikle çok az değer katmaktadır.

Ayrıca, Java VM'nin özel durumları denetlemediğini belirtmek gerekir - yalnızca Java derleyicisi bunları kontrol eder ve istisna bildirimlerini değiştiren sınıf dosyaları çalışma zamanında uyumludur. Java VM güvenliği, işaretli özel durumlar tarafından iyileştirilmez, yalnızca kodlama stili ile geliştirilir.


4
Argümanınız kendisine karşı çıkıyor. "Bazen bir istisna yakalamanız gerekir" ve API kalitesi genellikle düşükse, denetlenmiş istisnalar olmadan tasarımcının belirli bir yöntemin yakalanması gereken bir istisna verip vermediğini belgelemeyi ihmal edip etmediğini bilemezsiniz. Bunu AcmeExceptionyerine atmak AcmeFileNotFoundErrorve neyi yanlış yaptığınızı ve nerede yakalamanız gerektiğini bulmak için iyi şanslar. İşaretli istisnalar programcılara kötü API tasarımına karşı bir koruma sağlar.
Eva

1
Java kitaplığı tasarımı ciddi hatalar yaptı. 'İşaretli istisnalar' tahmin edilemeyen ve kurtarılabilir olasılıklar içindi - dosya bulunamadı, bağlantı kurulamıyor gibi. Hiçbir zaman düşük seviyeli sistemik başarısızlık için tasarlanmamışlardı. Denetlenecek bir dosyayı açmaya zorlamak iyi olurdu, ancak tek bir bayt yazma / SQL sorgusu yürütme vb. İçin herhangi bir mantıklı yeniden deneme veya kurtarma yoktur. Yeniden deneme veya kurtarma "iş" veya "isteğinde doğru şekilde işlenir ", istisnaları kontrol eden seviye anlamsızca zorlaşıyor. literatejava.com/exceptions/…
Thomas W

17

Son üç yıldır nispeten karmaşık uygulamalarda birkaç geliştirici ile çalışıyorum. Kontrol Edilmiş İstisnalar'ı düzgün hata işleme ile sıkça kullanan ve olmayan bazılarını kullanan bir kod tabanımız var.

Şimdiye kadar, Kontrol Bankası İstisnaları ile kod tabanı ile çalışmak daha kolay buldum. Başka birinin API'sini kullanırken, kodu arayarak ve düzgün bir şekilde işlediğimde, oturum açarak, görüntüleyerek veya yok sayarak tam olarak ne tür hata koşullarını bekleyebileceğimi görmek güzel (Evet, görmezden gelmek için geçerli durumlar var) ClassLoader uygulaması gibi istisnalar). Bu kodu kurtarmak için bir fırsat yazıyorum verir. Önbelleklenene ve bazı genel hata işleme kodlarıyla işlenene kadar çoğalttığım tüm çalışma zamanı özel durumları. Gerçekten belirli bir düzeyde işlemek istemediğim veya bir programlama mantığı hatası olduğunu düşündüğüm kontrol edilmiş bir istisna bulduğumda, bunu bir RuntimeException içine sarar ve kabarmasına izin veririm. Asla, iyi bir sebep olmadan bir istisnayı yutmayın (ve bunu yapmak için iyi nedenler oldukça azdır)

İstisnaları kontrol etmeyen kod temeli ile çalıştığımda, bana bazı şeyleri korkunç bir şekilde kırabilen işlevi çağırırken ne bekleyebilirim, önce elimi bilmek biraz zorlaştırıyor.

Bu elbette bir tercih ve geliştirici beceri meselesidir. Programlama ve hata işlemenin her iki yolu da eşit derecede etkili olabilir (veya etkisiz olabilir), bu yüzden Tek Yön olduğunu söyleyemem.

Sonuçta, özellikle çok sayıda geliştiriciye sahip büyük projelerde, Kontrol Edilen İstisnalar ile çalışmayı daha kolay buluyorum.


6
Yaparım. Benim için onlar bir sözleşmenin önemli bir parçası. API belgelerinde ayrıntılı bilgi almak zorunda kalmadan, hata senaryolarının olasılığını hızlı bir şekilde bilebilirim.
Wayne Hartman

2
Katılıyorum. Şebeke aramaları yapmaya çalıştığımda .Net'te kontrol edilen istisnaların gerekliliğini yaşadım. Bir ağ hıçkırıkının herhangi bir anda olabileceğini bilerek, bu senaryo için özel olarak yakalamam gereken istisnayı bulmak için API'ların tüm belgelerini okumak zorunda kaldım. C # istisnaları kontrol etseydi, hemen bilebilirdim. Diğer C # geliştiricileri muhtemelen uygulamanın basit bir ağ hatasından çökmesine izin verirdi.
AxiomaticNexus

13

İstisna kategorileri

İstisnalardan bahsederken daima Eric Lippert'in Vexing istisnaları blog makalesine geri dönüyorum . Bu kategorilere istisnalar ekler:

  • Ölümcül - Bu istisnalar sizin hatanız değildir : o zaman önleyemezsiniz ve onları mantıklı bir şekilde ele alamazsınız. Örneğin, OutOfMemoryErrorveya ThreadAbortException.
  • Boneheaded - Bu istisnalar sizin hatanızdır : onları engellemeliydiniz ve kodunuzdaki hataları temsil ederler. Örneğin ArrayIndexOutOfBoundsException, NullPointerExceptionveya herhangi biri IllegalArgumentException.
  • Vexing - Bu istisnalar istisnai değildir , hatanız değil , onları önleyemezsiniz, ancak onlarla başa çıkmanız gerekir. Bunlar atma gibi, genellikle talihsiz bir tasarım kararın sonucu NumberFormatExceptiongelen Integer.parseIntyerine sağlama Integer.tryParseIntayrıştırma failure bir Boole false döner yöntem.
  • Dışsal - Bu istisnalar genellikle istisnadır , sizin hatanız değil, onları (makul olarak) önleyemezsiniz, ancak bunları ele almalısınız . Örneğin FileNotFoundException,.

Bir API kullanıcısı:

  • ölümcül veya kemiksiz istisnaları ele almamalıdır .
  • can sıkıcı istisnaları ele almalı , ancak ideal bir API'de olmamalıdır.
  • dışsal istisnaları ele almalıdır .

Kontrol edilen istisnalar

API kullanıcısının belirli bir istisnayı ele alması gerektiği , yöntemin arayan ile arayan arasındaki arasındaki sözleşmenin bir parçasıdır. Sözleşme, diğer hususların yanı sıra şunları belirtir: arayanın beklediği argümanların sayısı ve türleri, arayanın bekleyebileceği dönüş değeri türü ve arayanın işlemesi beklenen istisnalar .

Yana üzücü istisnalar bir API içerisinde bulunmaması gereken, sadece bu eksojen istisnalar gereken istisnalar kontrol yöntemin sözleşmesinin bir parçası olmak için. Nispeten az sayıda istisna dışsaldır , bu nedenle herhangi bir API'nin kontrol edilen istisnaları nispeten az olmalıdır.

İşaretli bir istisna, ele alınması gereken bir istisnadır . Bir istisnayı ele almak onu yutmak kadar basit olabilir. Orada! İstisna ele alınır. Dönemi. Geliştirici bu şekilde işlemek istiyorsa, iyi. Ancak istisnayı görmezden gelemez ve uyarılır.

API sorunları

Ancak, can sıkıcı ve ölümcül istisnaları kontrol eden herhangi bir API (örn. JCL), API kullanıcılarına gereksiz yük bindirecektir. Böyle istisnaları var işlenecek, ancak istisna ya da ilk etapta bir istisna edilmiş olmamalıdır, ya da taşırken hiçbir şey yapılamayacağı böylece yaygındır. Ve bu kontrol istisnalar nefret Java geliştiricileri neden olur.

Ayrıca, birçok API'nin uygun bir istisna sınıfı hiyerarşisi yoktur, bu da her türlü eksojen olmayan istisna nedeninin tek bir kontrol edilen istisna sınıfı (örn. IOException) Tarafından temsil edilmesine neden olur . Bu da Java geliştiricilerinin denetlenen istisnalardan nefret etmesine neden olur.

Sonuç

Dışsal istisnalar, sizin hatanız olmayan, önlenemeyen ve ele alınması gereken istisnalardır. Bunlar atılabilecek tüm istisnaların küçük bir alt kümesini oluşturur. API'ların yalnızca ekzojen istisnaları kontrol etmesi ve diğer tüm istisnaların işareti kaldırılmış olması gerekir. Bu, daha iyi API'ler oluşturacak, API kullanıcısına daha az yük bindirecek ve bu nedenle kontrol edilemeyen istisnaların tümünü yakalama, yutma veya yeniden inceleme ihtiyacını azaltacaktır.

Bu yüzden Java ve denetlenen istisnalarından nefret etmeyin. Bunun yerine, işaretli istisnaları aşırı kullanan API'lardan nefret edin.


Ve bir hiyerarşiye sahip olmadan onları yanlış kullanın.
TofuBeer

1
FileNotFound ve bir JDBC / ağ bağlantısı kurmak , öngörülebilir ve (mümkün) kurtarılabilir olduğundan , beklenmedik durumlardır ve kontrol edilmesi gereken istisnalardır. Diğer birçok IOExceptions, SQLExceptions, RemoteException vb öngörülemeyen ve kurtarılamayan hatalardır ve çalışma zamanı istisnaları olmalıdır . Yanlış Java kitaplığı tasarımı nedeniyle, hepimiz bu hatayla karşılaştık ve şimdi çoğunlukla Spring & Hibernate'i (tasarımlarını doğru yapan) kullanıyoruz.
Thomas W

Genellikle "yönlendirme" olarak adlandırmak istemeyeceğiniz halde, genellikle başlı istisnaları ele almalısınız. Örneğin, bir web sunucusunda, bunları kaydeder ve kullanıcıya 500'ü gösteririm. İstisna beklenmedik olduğundan, hata düzeltmesinden önce yapabileceğim her şey var.
maaartinus

6

Tamam ... Kontrol edilen istisnalar ideal değildir ve bazı uyarılar vardır, ancak bir amaca hizmet ederler. Bir API oluştururken, bu API'nın sözleşmeye bağlı belirli arıza durumları vardır. Kontrol edilen istisnalar kullanılmıyorsa, Java gibi güçlü statik olarak yazılan bir dil bağlamında, hata olasılığını iletmek için geçici belgelere ve kurallara güvenmek gerekir. Bunu yapmak, derleyicinin hataya neden olabileceği tüm faydaları ortadan kaldırır ve tamamen programcıların iyi niyetine bırakılırsınız.

Yani, C # 'da yapıldığı gibi Kontrol edilmiş istisna kaldırılır, sonra nasıl programlı ve yapısal olarak hata olasılığını iletebilir? Bu ve bu tür hataların meydana gelebileceği ve ele alınması gereken müşteri kodunu nasıl bildiririm?

İşaretli istisnalarla uğraşırken her türlü dehşeti duyuyorum, yanlış kullanıldılar, bu kesin ama aynı zamanda kontrol edilmemiş istisnalar da var. API'lerin birçok katman derinliğinde istiflendiğinde birkaç yıl bekleyin ve başarısızlıkları iletmek için bir tür yapılandırılmış yolun geri dönüşü için yalvarıyorsunuz.

İstisna, API katmanlarının altında bir yere atıldığında ve sadece kabartıldığından, hiç kimse bu hatanın oluşmasının bile mümkün olduğunu bilmediğinden, bu, arama kodu çok makul bir hata olmasına rağmen attı (örneğin, VogonsTrashingEarthExcept'in aksine FileNotFoundException ... bu durumda, işlemek için hiçbir şey kalmadığımız için önemli olup olmadığı önemli değildir).

Birçoğu, dosyayı yükleyememenin neredeyse her zaman süreç için dünyanın sonu olduğunu ve korkunç ve acı verici bir ölümle ölmesi gerektiğini savundu. Yani evet .. emin ... tamam .. bir şey için bir API oluşturmak ve bir noktada dosya yükler ... Ben dedi ki API kullanıcı olarak sadece cevap verebilir ... "Kim ne zaman sen karar vermek için cehennem vardır program çökmeli! " Tabii İstisnaların ortaya çıktığı ve iz bırakmadığı veya EletroFlabbingChunkFluxManifoldChuggingException'ın Marianna açmasından daha derin bir yığın izine sahip olduğu seçimi göz önüne alındığında, istisna ile başa çıkmak için arzu edilen yol olduğu anlamına gelir. ? Ortada bir yerde olamazız, istisnanın her seferinde yeni bir soyutlama seviyesine geçtiği her seferinde yeniden düzenlenir ve sarılır, böylece aslında bir şey ifade eder mi?

Son olarak, gördüğüm argümanın çoğu "İstisnalarla uğraşmak istemiyorum, birçok insan istisnalarla uğraşmak istemiyor. Kontrol edilen istisnalar beni onlarla başa çıkmaya zorluyor, böylece kontrol edilen istisnadan nefret ediyorum" goto cehennemin uçurumuna düşmek sadece aptalca ve sürahi ve vizyondan yoksundur.

İşaretli istisnayı ortadan kaldırırsak, işlevler için dönüş türünü de ortadan kaldırabilir ve her zaman bir "anytype" değişkeni döndürebiliriz ... Bu, hayatı şimdi çok daha basit hale getirirdi değil mi?


2
Bir blok içindeki yöntem çağrılarının hiçbirinin (veya herhangi bir) kontrol edilen istisnaları atmasının beklenmediğini ve bu tür istisnaların otomatik olarak sarılıp yeniden düzenlenmesinin bildirici bir yolu varsa, kontrol edilen istisnalar yararlı olacaktır. İstisna işleme hızı için çağrı hızı / dönüşü üzerinden işlem gören kontrol edilen istisnaları atma olarak bildirilen yöntemlere yapılan çağrılar daha da yararlı olabilir (böylece beklenen istisnalar neredeyse normal program akışı kadar hızlı ele alınabilir). Bununla birlikte, şu anda hiçbir durum geçerli değildir.
Supercat

6

Gerçekten de, bir yandan kontrol edilen istisnalar programınızın sağlamlığını ve doğruluğunu artırır (arayüzlerinizin doğru bildirimlerini yapmak zorunda kalırsınız - bir yöntemin attığı istisnalar temelde özel bir dönüş türüdür). Öte yandan, istisnalar "patladığında" olduğundan, istisnaları değiştirdiğinizde çok sayıda yöntemi (tüm arayanlar ve arayanların arayanlar vb.) Değiştirmeniz gerekir. yöntem atar.

Java'da denetlenen istisnalar ikinci sorunu çözmez; C # ve VB.NET bebeği banyo suyuyla dışarı atar.

Orta yolu kullanan hoş bir yaklaşım, bu OOPSLA 2005 belgesinde (veya ilgili teknik raporda) açıklanmaktadır .

Kısacası, şunu söylemenize izin verir: method g(x) throws like f(x)yani g, f'nin attığı tüm istisnaları atar. Voila, basamaklı değişiklikler sorunu olmadan istisnaları kontrol etti.

Akademik bir makale olmasına rağmen, kontrol edilen istisnaların yararlarını ve dezavantajlarını açıklamak için iyi bir iş çıkardığı için okumanızı (bir kısmını) yapmanızı öneririm.


5

Bu, kontrol edilmiş istisnalar kavramına karşı bir argüman değil, ancak Java'nın onlar için kullandığı sınıf hiyerarşisi bir ucube gösterisi. Biz her zaman sadece "istisnalar" diyoruz - ki bu doğru, çünkü dil belirtimi onlara da öyle diyor - ama tip sisteminde bir istisna nasıl adlandırılır ve temsil edilir?

ExceptionBirinci sınıfa göre ? Hayır Hayýýr Exceptionler istisnalar vardır ve aynı şekilde istisnalar Exceptionler olan bu istisnalar hariç değil Exception diğer istisnalar nedeniyle, s aslında Erroristisna başka türde hariç hiçbir zaman olmamalıdır ekstra olağanüstü istisna bir tür olan, s ve bazen yakalamanız gerekmediği zaman asla yakalanmamanız gerekir. Sadece bu değil, çünkü ne istisna ne Exceptionde Errorsadece Throwableistisna olan diğer istisnaları da tanımlayabilirsiniz .

Bunlardan hangisi "kontrol edilen" istisnalar? Throwables, denetlenmeyen istisnalar, ancak Errordenetlenmeyen istisnalar olan s olmaları dışında, ve sonra Exceptions olan Throwableve denetlenen özel durumun ana türü olan s'ler de var, ancak bunun da bir istisnası var, yani aynı zamanda RuntimeExceptionkontrolsüz bir istisnadır.

Ne işe RuntimeExceptionyarar? Eh, adından da anlaşılacağı gibi, bunlar tüm Exceptions gibi istisnalardır ve aslında tüm istisnalar gibi çalışma zamanında gerçekleşirler, ancak RuntimeExceptions, diğer çalışma zamanı Exceptions ile karşılaştırıldığında istisna değildir, çünkü bunların dışında olması gerekmemektedir. aptalca bir hata yaptığınızda, RuntimeExceptions asla Errors olmamasına rağmen , son derece hatalı olan ama aslında Errors olmayan şeyler içindir . Haricinde RuntimeErrorExceptiongerçekten hangi RuntimeExceptioniçin Errors. Ancak yine de tüm istisnaların hatalı durumları temsil etmesi gerekmez mi? Evet, hepsi. Haricinde ThreadDeathdokümantasyon bir "normal bir durum" olduğunu ve o' olduğunu açıkladığı gibi, son derece sakatlıklar istisna,Error

Her neyse, tüm istisnaları ortadan Errors'ye ayırdığımızdan (istisnai yürütme istisnaları içindir, bu Exceptionyüzden kontrol edilmez) ve s (daha az istisnai yürütme hataları içindir, bu yüzden olmadığı zamanlar kontrol edilir) birkaç istisna dışında her türlü. Yani ihtiyacımız var IllegalAccessErrorve IllegalAccessException, ve InstantiationErrorve InstantiationException, ve NoSuchFieldErrorve NoSuchFieldException, ve NoSuchMethodErrorve NoSuchMethodException, ve ZipErrorve ZipException.

Bir istisna işaretlendiğinde bile, derleyiciyi aldatmanın ve kontrol edilmeden atmanın her zaman (oldukça kolay) yolları vardır. Eğer sen yaparsanız bir olsun ki UndeclaredThrowableExceptionbir şekilde kusmak olabilir, diğer durumlarda, dışında, UnexpectedExceptionya da bir UnknownException(ilgisiz olan UnknownErrorsadece "ciddi istisnalar" için olan) veya ExecutionExceptionveya bir InvocationTargetExceptionveya bir ExceptionInInitializerError.

Oh, ve Java 8'in şık yeni özelliklerini unutmamalıyız UncheckedIOException, bu da G / Ç hatalarının neden olduğu RuntimeExceptionkontrol edilen IOExceptionistisnaları (bu IOErrorolmasına rağmen istisnalara neden olmayan) sararak istisna denetimi kavramını pencereden dışarı atmanıza izin vermek için tasarlanmış bir istisnadır. Ayrıca) kullanımı son derece zordur ve bu nedenle kontrol edilmemeleri gerekir.

Teşekkürler Java!


2
Söyleyebildiğim kadarıyla, bu cevap sadece "Java'nın istisnaları bir karışıklıktır" diyor, ironi, tartışmasız komik bir şekilde. Görünmeyen şey, programcıların neden bu şeylerin nasıl çalışması gerektiğini anlamaya çalışmaktan kaçınma eğiliminde olduğunu açıklamaktır. Ayrıca, gerçek hayattaki durumlarda (en azından başa çıkma şansım olanlar), programcılar kasıtlı olarak hayatlarını zorlaştırmaya çalışmazsa, istisnalar tanımladığınız kadar karmaşık değildir.
CptBartender

5

Sorun

İstisna işleme mekanizması ile gördüğüm en kötü sorun, büyük ölçüde kod çoğaltma tanıtımıdır ! Dürüst olalım: Çoğu zaman% 95'teki projelerde, geliştiricilerin istisna dışında gerçekten tek yapması gereken, bunu bir şekilde kullanıcıya (ve bazı durumlarda geliştirme ekibine, örneğin bir e-posta göndererek) iletmektir. -posta yığını izleme ile). Bu nedenle, istisnanın işlendiği her yerde genellikle aynı satır / kod bloğu kullanılır.

Bir tür kontrol edilen istisna için her catch bloğunda basit günlük kaydı yaptığımızı varsayalım:

try{
   methodDeclaringCheckedException();
}catch(CheckedException e){
   logger.error(e);
}

Bu yaygın bir istisna ise, daha büyük bir kod tabanında birkaç yüzlerce try-catch bloğu bile olabilir. Şimdi, konsol günlüğü yerine açılır pencere iletişim kutusu tabanlı özel durum işleme özelliğini kullanmamız gerektiğini veya geliştirme ekibine ek olarak bir e-posta göndermeye başlamamız gerektiğini varsayalım.

Bir dakika ... koddaki yüzlerce konumu gerçekten düzenleyecek miyiz ?! Benim fikrimi anladın :-).

Çözüm

Bu sorunu ele almak için yaptığımız şey, istisna işlemeyi merkezileştirmek için istisna işleyicileri kavramını (daha sonra EH olarak anlatacağım) tanıtmaktı . İstisnaları olması gereken her sınıfa bir istisna işleyici örneği Bağımlılık Enjeksiyonu çerçevemiz tarafından enjekte edilir . Bu nedenle, kural dışı durum işleme tipik modeli şu şekildedir:

try{
    methodDeclaringCheckedException();
}catch(CheckedException e){
    exceptionHandler.handleError(e);
}

İstisna işlememizi özelleştirmek için kodu yalnızca tek bir yerde (EH kodu) değiştirmemiz gerekiyor.

Elbette daha karmaşık vakalar için EH'lerin birkaç alt sınıfını uygulayabilir ve DI çerçevemizin bize sağladığı özelliklerden yararlanabiliriz. DI çerçeve yapılandırmamızı değiştirerek, EH uygulamasını küresel olarak kolayca değiştirebilir veya özel istisna işleme ihtiyaçları olan sınıflara (örneğin, Guice @Named ek açıklamasını kullanarak) EH'nin belirli uygulamalarını sağlayabiliriz.

Bu şekilde, uygulamanın geliştirme ve sürüm sürümünü istisna işleme davranışını ayırt edebiliriz (örn. Geliştirme - hatayı günlüğe kaydetme ve uygulamayı durdurma, hatayı daha fazla ayrıntıyla kaydetme ve uygulamanın yürütülmesine devam etmesine izin verme).

Son bir şey

Son olarak, bir tür üst düzey istisna işleme sınıfına gelene kadar istisnalarımızı "yukarı" geçerek aynı tür bir merkezileşme elde edilebilir. Ancak bu, kodlarımızın ve yöntemlerimizin imzalarının dağınıklığına yol açar ve bu konuda başkaları tarafından belirtilen bakım sorunlarını ortaya çıkarır.


6
İstisnalar, onlarla yararlı bir şey yapmak için icat edilir. Bunları bir günlük dosyasına yazmak veya güzel bir pencere oluşturmak yararlı değildir, çünkü orijinal sorun bununla çözülmez. Yararlı bir şey yapmak için farklı bir çözüm stratejisi denemek gerekir. Örnekler: Verilerimi sunucu AI'dan alamıyorsam, B sunucusunda deneyin. Veya A algoritması bir yığın taşması üretiyorsa, çok daha yavaş ama başarılı olabilecek B algoritmasını denerim.
ceving

2
@ceving Evet, teoride her şey yolunda ve doğru. Ama şimdi kelime pratiğine geri dönelim. Lütfen gerçek kelimeli projenizde ne sıklıkta yaptığınızı gerçekten dürüstçe cevaplayın? catchBu gerçek projedeki blokların hangi kısmı hariç tutulanlarla gerçekten "yararlı" bir şey yapar? % 10 iyi olurdu. İstisnalar oluşturan olağan problemler, mevcut olmayan dosyadan yapılan konfigürasyonu okumaya çalışmak gibidir, OutOfMemoryErrors, NullPointerExceptions, veritabanı kısıtlama bütünlüğü hataları vb. Gerçekten hepsinden zarif bir şekilde kurtarmaya mı çalışıyorsunuz? Sana inanmıyorum :). Genellikle iyileşmenin bir yolu yoktur.
Piotr Sobczyk

3
@PiotrSobczyk: Bir program, dava talebinin bir sonucu olarak bazı eylemlerde bulunursa ve işlem, sistem durumunda hiçbir şeye zarar vermeyen bir şekilde başarısız olursa, kullanıcıya işlemin tamamlanamayacağını bildirmek son derece yararlıdır. durumla başa çıkma yolu. C # ve .net'teki istisnaların en büyük başarısızlığı, sistem durumundaki herhangi bir şeyin hasar görüp görmediğini tespit etmenin tutarlı bir yolu olmamasıdır.
supercat

4
Doğru, @PiotrSobczyk. Çoğu zaman, bir özel duruma yanıt olarak yapılacak tek doğru işlem, işlemi geri almak ve bir hata yanıtı döndürmektir. "İstisna çözme" fikirleri sahip olmadığımız (ve olmamamız gereken) bilgi ve otoriteyi ima eder ve kapsüllemeyi ihlal eder. Uygulamamız bir DB değilse , DB'yi düzeltmeye çalışmamalıyız . Temiz bir şekilde başarısız olmak ve hatalı veri yazmaktan kaçınmak, ceving, yeterince faydalıdır .
Thomas W

@PiotrSobczyk Dün, bir "nesne okunamadı" istisnasıyla uğraştım (bu sadece altta yatan veritabanı yazılımdan önce güncellendiği için ortaya çıkacaktır - asla olmamalı, ancak insan hatası nedeniyle bir olasılıktır) veritabanının tarihsel sürümü, nesnenin eski bir sürümünü göstermesi garantidir.
İsimsiz

4

Anders, kontrol edilen istisnaların tuzaklarından ve bunları Yazılım Mühendisliği radyosunun 97. bölümünde neden C # 'dan çıkardığından bahsediyor .


4

C2.com'daki yazım hala orijinal biçiminden farklı değil: CheckedExceptionsAreIncompatibleWithVisitorPattern

Özetle:

Ziyaretçi Paterni ve akrabaları, dolaylı arayan ve arayüz uygulamasının bir istisna hakkında bildiği ancak arayüz ve doğrudan arayanın bilmeyen bir kütüphane oluşturduğu bir arayüz sınıfıdır.

CheckedExceptions'ın temel varsayımı, beyan edilen istisnaların, bu bildirimle bir yöntemi çağıran herhangi bir noktadan atılabileceğidir. VisitorPattern bu varsayımın hatalı olduğunu açıklar.

Bu gibi durumlarda kontrol edilen istisnaların nihai sonucu, derleyicinin kontrol edilen istisna sınırlamasını çalışma zamanında esasen kaldıran bir sürü işe yaramaz koddur.

Altta yatan soruna gelince:

Genel fikrim, üst düzey işleyicinin istisnayı yorumlaması ve uygun bir hata mesajı görüntülemesi gerektiğidir. Neredeyse her zaman IO istisnaları, iletişim istisnaları (bazı nedenlerden dolayı API'lar ayırt eder) veya görevle ilgili önemli hatalar (program hataları veya yedekleme sunucusunda ciddi sorun) görürüm, bu nedenle, sunucu sorunu.


1
Arabirimde DAGNodeException gibi bir şey olmalı, sonra IOException'ı yakalayın ve bir DAGNodeException öğesine dönüştürün: public void call (DAGNode argception) DAGNodeException;
TofuBeer

2
@TofuBeer, bu tam olarak benim açımdan. Sürekli sarma ve ambalajdan çıkarma istisnalarının, kontrol edilen istisnaları kaldırmaktan daha kötü olduğunu düşünüyorum.
Joshua

2
Peki o zaman tamamen katılmıyorum ... ama makaleniz hala bir çalışma zamanı istisnası atıldığında uygulamanızın kullanıcıya bir yığın izlemesi görüntülemesini nasıl durduracağınıza dair asıl soruya cevap vermiyor.
TofuBeer

1
@TofuBeer - Başarısız olduğunda, kullanıcıya başarısız olduğunu söylemek doğrudur! 'Null' veya eksik / yanlış verilerle başarısızlığı "aşmak" dışında alternatifiniz nedir? Başarılı olduğunu iddia etmek bir şey, sadece işleri daha da kötüleştiriyor. Yüksek güvenilirlikli sistemlerde 25 yıllık tecrübeyle, yeniden deneme mantığı sadece dikkatli bir şekilde ve gerektiğinde kullanılmalıdır. Ayrıca, bir Ziyaretçinin kaç kez yeniden denemenize bakılmaksızın büyük olasılıkla tekrar başarısız olmasını beklerim. Bir uçağı uçurmuyorsanız, aynı algoritmanın ikinci bir sürümüne geçmek pratik değildir ve imkansızdır (ve yine de başarısız olabilir).
Thomas W

4

Sadece cevaplanmamış soruyu ele almaya çalışmak için:

İstisna alt sınıfları yerine RuntimeException alt sınıflarını atarsanız ne yakalamanız gerektiğini nasıl anlarsınız?

Soru, IMHO'nun muhakemesini içeriyor. API'nın size ne attığını söylemesi, her durumda onunla aynı şekilde uğraştığınız anlamına gelmez. Başka bir deyişle, yakalamanız gereken istisnalar, istisnayı atan bileşeni kullandığınız bağlama göre değişir.

Örneğin:

Bir veritabanı için bir bağlantı test cihazı yazıyorsam veya XPath'a girilen kullanıcının geçerliliğini kontrol etmek için bir şey yazıyorsam, muhtemelen işlem tarafından atılan tüm kontrol edilen ve kontrol edilmeyen istisnaları yakalamak ve raporlamak isterim.

Ancak, ben bir işleme motoru yazıyorum, ben muhtemelen bir XPathException (işaretli) bir NPE ile aynı şekilde tedavi edecek: Ben işçi iş parçacığının üstüne kadar çalışmasına izin, o toplu işin geri kalanını atlamak, günlük sorunu (veya teşhis için bir destek bölümüne gönderin) ve kullanıcının destekle iletişim kurması için geri bildirim bırakın.


2
Kesinlikle. Kolay ve anlaşılır, istisna işleme şekli. Dave'in dediği gibi, doğru istisna yönetimi normalde yüksek bir seviyede yapılır . "Erken atın, geç yakalayın" ilkesidir. Kontrol edilen istisnalar bunu zorlaştırır.
Thomas W

4

Bu makale , okuduğum Java'da istisna işleme hakkındaki en iyi metindir.

Kontrol edilen istisnalar üzerinde denetlenmemiş olanları tercih eder, ancak bu seçim çok kapsamlı bir şekilde açıklanır ve güçlü argümanlara dayanır.

Burada çok fazla makale içeriğinden alıntı yapmak istemiyorum (bir bütün olarak okumak en iyisidir), ancak bu iş parçacığından denetlenmeyen istisna savunucularının argümanlarının çoğunu kapsar. Özellikle (oldukça popüler gibi görünen) bu argüman ele alınmaktadır:

İstisna, API katmanlarının altında bir yere atıldığında ve sadece kabartıldığından, hiç kimse bu hatanın oluşmasının bile mümkün olduğunu bilmediğinden, bu, arama kodu çok makul bir hata olmasına rağmen attı (örneğin, VogonsTrashingEarthExcept'in aksine FileNotFoundException ... bu durumda, işlemek için hiçbir şey kalmadığımız için önemli olup olmadığı önemli değildir).

Yazar "yanıtlar":

Tüm çalışma zamanı istisnalarının yakalanmaması ve uygulamanın en üst kısmına yayılmasına izin verilmemesi gerektiğini kabul etmek kesinlikle yanlıştır. (...) Ayrı ayrı ele alınması gereken her istisnai durum için - sistem / iş gereksinimleri tarafından - programcılar, nerede yakalanacağına ve durum yakalandıktan sonra ne yapılacağına karar vermelidir. Bu, derleyici uyarısına bağlı olarak değil, uygulamanın gerçek ihtiyaçlarına göre kesinlikle yapılmalıdır. Diğer tüm hataların, kaydedilecekleri en üstteki işleyiciye serbestçe yayılmasına izin verilmelidir ve zarif (belki de sonlandırma) bir eylem gerçekleştirilecektir.

Ve ana düşünce veya makale:

Yazılımda hata işleme söz konusu olduğunda, yapılabilecek tek güvenli ve doğru varsayım, var olan her alt programda veya modülde kesinlikle bir arıza olabileceğidir!

Yani " kimse bu hatanın oluşmasının mümkün olduğunu bilmiyorsa " bu projede bir sorun var demektir. Böyle bir istisna, yazarın önerdiği gibi, en azından en genel istisna işleyici tarafından ele alınmalıdır (örneğin, daha spesifik işleyiciler tarafından ele alınmayan tüm İstisnaları ele alan).

Çok üzücü pek çok insan bu harika makaleyi keşfediyor gibi görünmüyor :-(. Hangi yaklaşımın tereddüt eden herkese biraz zaman ayırıp okumak için daha iyi olduğunu tavsiye ederim.


4

Kontrol edilen istisnalar orijinal hallerinde başarısızlıklardan ziyade beklenmedik durumları ele alma girişimi idi. Övgüye değer hedef, öngörülebilir belirli noktaları vurgulamak (bağlantı kurulamıyor, dosya bulunamadı, vb.) Ve geliştiricilerin bunları ele almasını sağlamaktı.

Asla orijinal konsepte dahil edilmeyen şey, çok çeşitli sistemik ve kurtarılamayan arızaların açıklanmasına zorlamaktı. Bu hataların, kontrol edilen istisnalar olarak beyan edilmesi hiçbir zaman doğru olmamıştır.

Hatalar genellikle kodda mümkündür ve EJB, web & Swing / AWT kapsayıcıları, en dıştaki "başarısız istek" istisna işleyicisi sağlayarak bunu zaten karşılar. En temel doğru strateji, işlemi geri almak ve bir hata döndürmektir.

Önemli bir nokta, çalışma zamanı ve kontrol edilen istisnaların işlevsel olarak eşdeğer olmasıdır. İstisnaların yapabileceği, çalışma zamanı istisnalarının yapamayacağı herhangi bir işlem veya kurtarma yoktur.

“İşaretli” istisnalara karşı en büyük argüman, istisnaların çoğunun düzeltilemeyeceğidir. Basit gerçek şu ki, kırılan kod / alt sisteme sahip değiliz. Uygulamayı göremiyoruz, bundan sorumlu değiliz ve düzeltemiyoruz.

Bizim uygulama bir DB değilse .. biz denemek ve DB düzeltmek gerekir. Bu , kapsülleme ilkesini ihlal eder .

Özellikle sorunlu olan JDBC (SQLException) ve EJB için RMI (RemoteException) alanları olmuştur. Orijinal “kontrol edilen istisna” kavramına göre sabitlenebilir olasılıkları tanımlamak yerine, bu sabitlenemeyen yaygın sistemik güvenilirlik sorunlarının, aslında düzeltilemez, yaygın olarak ilan edilmesi.

Java tasarımındaki diğer ciddi kusur, istisna işlemenin mümkün olan en yüksek "iş" veya "talep" düzeyine doğru bir şekilde yerleştirilmesi gerektiğiydi. Buradaki prensip "erken at, geç yakala" dır. İşaretli istisnalar çok az şey yapar, ancak bunu engeller.

Java'da, önemli bir oranın (% 40 +) yanlış kodlandığı binlerce, hiçbir şey yapmadan deneme yakalama bloğu gerektirme konusunda bariz bir sorunumuz var. Bunların neredeyse hiçbiri gerçek bir kullanım veya güvenilirlik uygulamamaktadır, ancak büyük kodlama yükü uygulamaktadır.

Son olarak, "işaretli istisnalar" FP fonksiyonel programlama ile hemen hemen uyumsuzdur.

"Hemen ele alın" konusundaki ısrarları, hem "geç yakala" istisna işleme en iyi uygulamaları ve döngüler / veya kontrol akışını soyutlayan FP yapıları ile çelişmektedir.

Birçok kişi, kontrol edilen istisnaları "ele almak" hakkında konuşuyor, ancak şapkalarından konuşuyorlar. Başarıyı taklit etmek için null, eksik veya yanlış verilerle başarısız olduktan sonra devam etmek hiçbir şeyi işlemez. En düşük formun mühendislik / güvenilirlik hatasıdır.

Temiz bir şekilde başarısız olmak, bir istisnayı ele almak için en temel doğru stratejidir. İşlemi geri almak, hatayı günlüğe kaydetmek ve kullanıcıya bir "başarısızlık" yanıtı bildirmek sağlam bir uygulamadır - ve en önemlisi, veritabanına yanlış iş verilerinin işlenmesini önler.

Özel durum işleme için diğer stratejiler, işletme, alt sistem veya istek düzeyinde "yeniden deneme", "yeniden bağlanma" veya "atlama" şeklindedir. Tüm bunlar genel güvenilirlik stratejileridir ve çalışma zamanı istisnaları dışında iyi / daha iyi çalışır.

Son olarak, hatalı verilerle çalışmaktan ziyade başarısız olmak çok tercih edilir. Devam etmek ya orijinal nedenden uzakta ikincil hatalara neden olur ve hata ayıklamak daha zordur; veya sonuçta hatalı verilerin işlenmesine neden olur. İnsanlar bunun için kovuluyor.

Bakınız:
- http://literatejava.com/exceptions/checked-exceptions-javas-biggest-mistake/


1
Demek istediğim etmektir düzgün başarısız genel strateji olarak. İşaretlenmeyen istisnalar, yakalama bloklarını araya sokmaya zorlamadıklarından yardımcı olur. Yakalama ve hata günlüğü, kod tabanı boyunca binlerce kez yanlış kodlanmak yerine (aslında hataları gizleyen şeydir) birkaç dış işleyiciye bırakılabilir . Keyfi başarısızlıklar için, kontrol edilmeyen istisnalar kesinlikle en doğrudur. Koşullu yükümlülükler - yetersiz fonlar gibi öngörülebilir sonuçlar - meşru olarak kontrol edilmeye değer istisnalar arasındadır.
Thomas W

1
Yukarıda cevabım buna değiniyor. İlk ve en önemlisi, 1) En dıştaki arıza giderici her şeyi yakalamalıdır. Bunun ötesinde, sadece belirli tanımlanmış sahalar için, 2) Beklenen belirli olasılıklar yakalanabilir ve ele alınabilir - hemen atıldıkları yerde. Bu, dosyaların bulunamadıkları noktada dosya bulunamadı, yetersiz fonlar vs. anlamına geliyor - daha yüksek değil. Kapsülleme ilkesi, dış katmanların içerideki arızaları anlamak / düzeltmekten sorumlu olamayacağı / sorumlu olamayacağı anlamına gelir. Üçüncüsü, 3) Diğer her şey dışa doğru atılmalıdır - mümkünse işaretlenmez.
Thomas W

1
En dıştaki işleyici İstisna yakalar, günlüğe kaydeder ve "başarısız" yanıtı döndürür veya bir hata iletişim kutusu gösterir. Çok basit, tanımlaması hiç zor değil. Mesele şu ki, hemen ve yerel olarak kurtarılamayan her istisna , kapsülleme ilkesi nedeniyle kurtarılamaz bir başarısızlıktır . Bilmesi gereken kod onu kurtaramazsa, istek genel olarak temiz ve düzgün bir şekilde başarısız olur. Doğru şekilde yapmanın doğru yolu budur.
Thomas W

1
Yanlış. En dıştaki işleyicinin işi temiz bir şekilde başarısız olmak ve hataları 'istek' sınırına kaydetmek. Bozuk istek düzgün şekilde başarısız oluyor, istisna bildirildi, iş parçacığı bir sonraki isteğe hizmet vermeye devam edebiliyor. Böyle bir dış işleyici Tomcat, AWT, Spring, EJB kapları ve Java 'ana' iş parçacığında standart bir özelliktir.
Thomas W

1
İstek sınırında veya en dıştaki işleyicide "gerçek hataları" bildirmek neden tehlikelidir ??? Sıklıkla doğru güvenilirlik mühendisliğinin gerçekten önemli olduğu sistem entegrasyonu ve güvenilirliğinde çalışıyorum ve bunu yapmak için "denetlenmeyen istisna" yaklaşımları kullanıyorum. Aslında tartıştığınız şeyden gerçekten emin değilim - aslında 3 ayını kontrolsüz istisna şekilde geçirmek, bunu hissetmek isteyebilir ve belki daha fazla tartışabiliriz. Teşekkürler.
Thomas W

4

Millet daha önce de belirtildiği gibi, Java bayt kodunda işaretli istisnalar mevcut değildir. Diğer sözdizimi denetimlerinin aksine basitçe bir derleyici mekanizmasıdır. Derlenmiş bir gereksiz koşul hakkında şikayetçi görüyorum gibi kontrol istisnaları çok görüyorum if(true) { a; } b;. Bu yardımcı olur, ancak bunu bilerek yapmış olabilirim, bu yüzden uyarılarınızı görmezden gelmeme izin verin.

İşin aslı şu ki, kontrol edilen istisnaları uygularsanız ve artık herkes sizin yaptığınız kural için nefret eden teminat hasarı ise, her programcıyı "doğru olanı yapmaya" zorlayamazsınız.

Kötü programları düzeltin! Onlara izin vermemek için dili düzeltmeye çalışmayın! Çoğu kişi için, "istisna hakkında bir şeyler yapmak" gerçekten kullanıcıya bunu anlatmaktır. Kullanıcıya kontrol edilmeyen bir istisna hakkında da bilgi verebilirim, bu yüzden işaretli istisna sınıflarınızı API'mdan uzak tutun.


Doğru, sadece erişilemeyen kod (bir hata üreten) ve öngörülebilir bir sonuçla şartlar arasındaki farkı vurgulamak istedim. Bu yorumu daha sonra kaldıracağım.
Holger

3

İşaretli istisnalarla ilgili bir sorun, bir arabirimin yöntemlerinde bile istisnaların genellikle bir arabirimin yöntemlerine eklenmesidir.

Kontrol edilen istisnalarla ilgili bir başka sorun da yanlış kullanım eğilimindedir. Bu mükemmel bir örnek olan java.sql.Connectionbireyin close()bir yöntem. Bağlantı ile işiniz bittiğini açıkça belirtmişSQLException olsanız bile, bir a atabilir . Hangi bilgiler umursadığınız close () iletebilir?

Genellikle, bir bağlantıyı kapattığımda () *, şöyle görünür:

try {
    conn.close();
} catch (SQLException ex) {
    // Do nothing
}

Ayrıca, beni çeşitli ayrıştırma yöntemlerinde ve NumberFormatException'da başlatma ... İstisnalar atmayan .NET'in TryParse'sini kullanmak çok daha kolay, Java'ya geri dönmek zorunda kalıyoruz (hem Java hem de Java kullanıyoruz) C # nerede çalışıyorum).

*Ek bir yorum olarak, PooledConnection'ın Connection.close () yöntemi bir bağlantıyı bile kapatmaz , ancak yine de işaretli bir istisna olması nedeniyle SQLException'ı yakalamanız gerekir.


2
Doğru, sürücülerden herhangi biri ... soru daha ziyade "programcı neden ilgilenmeli?" zaten veritabanına erişmeyi tamamladı. Belgeler, close () öğesini çağırmadan önce geçerli işlemi her zaman taahhüt etmeniz () veya geri almanız () gerektiği konusunda sizi uyarır.
Powerlord

Birçok kişi bir dosyayı kapatmanın bir istisna atamayacağını düşünüyor ... stackoverflow.com/questions/588546/… Önemli olacağı hiçbir durum olmadığından% 100 emin misiniz?
TofuBeer

Ben hiçbir durumlar olduğunu% 100 eminim ediyorum önemi ve arayan o olmaz bir try / catch koymak.
Martin

1
Kapanış bağlantıları ile mükemmel bir örnek Martin! Sadece sizi yeniden ifade edebilirim: Eğer bir bağlantı ile işimizi bitirdiğimizi açıkça ifade edersek, kapattığımızda neler olduğunu neden rahatsız etmeliyiz. Programcının istisna olup olmadığını gerçekten önemsemediği ve bununla ilgili kesinlikle doğru olduğu gibi daha fazla vaka var.
Piotr Sobczyk

1
@PiotrSobczyk: Bir işlemi başlattıktan sonra bağlantıyı kapatır, ancak onaylamaz ya da geri almazsa bazı SQL sürücüleri squawk yapar. IMHO, squawking, en azından squawking'in diğer istisnaların kaybolmasına neden olmayacağı durumlarda, sorunu sessizce görmezden gelmekten daha iyidir.
Supercat

3

Programcı , bir yöntemin doğru şekilde kullanabilmesi için atabileceği tüm istisnaları bilmelidir . Bu nedenle, onu sadece bazı istisnalar ile başının üzerine atmak, dikkatsiz bir programcının hatalardan kaçınmasına yardımcı olmaz.

İnce fayda külfetli maliyetten daha ağır basar (özellikle arayüz imzalarını sürekli değiştirmenin pratik olmadığı daha büyük, daha az esnek kod tabanlarında).

Statik analiz güzel olabilir, ancak gerçekten güvenilir statik analiz genellikle programcıdan sıkı bir çalışma gerektirir. Maliyet-fayda hesaplaması vardır ve derleme zamanı hatasına yol açan bir denetim için çubuğun yüksek ayarlanması gerekir. IDE'nin bir yöntemin hangi istisnaları atabileceği (kaçınılmaz olanlar da dahil) iletişim kurma rolünü üstlenmesi daha yararlı olacaktır. Zorla istisna beyanları olmadan belki de güvenilir olmayacak olsa da, istisnaların çoğu hala dokümantasyonda beyan edilecek ve bir IDE uyarısının güvenilirliği o kadar önemli değildir.


2

Bunun mükemmel bir soru olduğunu ve tartışmacı olmadığını düşünüyorum. Üçüncü taraf kütüphanelerin (genel olarak) kontrol edilmeyen istisnalar atması gerektiğini düşünüyorum . Bu, kütüphaneye bağımlılıklarınızı izole edebileceğiniz anlamına gelir (yani istisnalarını yeniden atmanız veya atmanız gerekmez Exception- genellikle kötü uygulama). Spring'in DAO katmanı bunun mükemmel bir örneğidir.

Onlar eğer Öte yandan, çekirdek Java API istisnalar genel olabilir kontrol edilmelidir şimdiye ele alınması. Al FileNotFoundExceptionya da (benim favorim) InterruptedException. Bu koşullar hemen hemen her zaman spesifik olarak ele alınmalıdır (örn. An ile InterruptedExceptionolan reaksiyonunuz, an ile olan reaksiyonunuzla aynı değildir IllegalArgumentException). İstisnalarınızın kontrol edilmiş olması, geliştiricileri bir koşulun işlenip işlenemeyeceğini düşünmeye zorlar. (Bu dedi, nadiren InterruptedExceptiondüzgün ele gördüm !)

Bir şey daha - a RuntimeExceptionher zaman "geliştiricinin yanlış bir şey yaptığı yerde" değildir. Bir enumkullanmayı denediğinizde ve oluşturduğunuzda yasadışı bir argüman istisnası atılır valueOfve bu addan hiçbiri yoktur enum. Bu mutlaka geliştirici tarafından bir hata değildir!


1
Evet, geliştirici bir hatadır. Açıkça doğru ismi kullanmadılar, bu yüzden geri dönüp kodlarını düzeltmek zorundalar.
AxiomaticNexus

@AxiomaticNexus Hiçbir aklı başında geliştirici enumüye adlarını kullanmaz, çünkü enumbunun yerine nesneleri kullanırlar. Yani yanlış bir isim, bir dışa aktarım dosyası ya da her neyse, sadece dışarıdan gelebilir. Bu tür isimlerle başa çıkmanın olası bir yolu MyEnum#valueOfIAE'yi aramak ve yakalamaktır. Başka bir yol önceden doldurulmuş kullanmaktır Map<String, MyEnum>, ancak bunlar uygulama detaylarıdır.
maaartinus

@maaartinus Enum üye adlarının dize dışarıdan gelmeden kullanıldığı durumlar vardır. Örneğin, her biriyle bir şeyler yapmak için tüm üyeler arasında dinamik olarak döngü oluşturmak istediğinizde. Ayrıca, ipin dışarıdan gelip gelmediği önemsizdir. Geliştirici, x dizesinin "MyEnum # valueOf" a iletilmesinden önce bir hata ile sonuçlanıp sonuçlanmayacağını bilmek için ihtiyaç duydukları tüm bilgilere sahiptir. X stringini "MyEnum # valueOf" dosyasına hataya neden olacak şekilde iletmek, geliştiricinin kısmında açıkça bir hata olacaktır.
AxiomaticNexus

2

İşte kontrol edilen istisnalara karşı bir argüman (joelonsoftware.com'dan):

Bunun nedeni, istisnaları, 1960'lardan beri zararlı kabul edilen "goto" lardan daha iyi bir şey olarak görmemin, bir kod noktasından diğerine ani bir sıçrama yaratmalarıdır. Aslında goto'lardan önemli ölçüde daha kötüdürler:

  • Kaynak kodunda görünmezler. İstisnalar atabilecek veya atmayacak fonksiyonlar dahil bir kod bloğuna bakıldığında, hangi istisnaların nereden atılabileceğini görmenin bir yolu yoktur. Bu, dikkatli kod incelemesinin bile potansiyel hataları göstermediği anlamına gelir.
  • Bir işlev için çok fazla olası çıkış noktası oluştururlar. Doğru kodu yazmak için, gerçekten işleviniz boyunca olası her kod yolunu düşünmeniz gerekir. İstisna oluşturabilen ve yerinde yakalamayan bir işlevi her aradığınızda, verileri aniden sonlanan işlevlerden kaynaklanan verileri tutarsız bir durumda bırakarak veya yapmadığınız diğer kod yollarında sürpriz hatalar için fırsatlar yaratırsınız. hakkında düşün.

3
+1 Yine de cevabınızdaki argümanı özetlemek isteyebilirsiniz? Program boyunca dağılmış olan rutinleriniz için görünmez gotolar ve erken çıkışlar gibidirler.
MarkJ

13
Bu genel olarak İstisnalara karşı bir argüman.
Ionuț G. Stan

10
aslında makaleyi okudun mu !! İlk olarak genel olarak istisnalardan bahseder, ikincisi "Kaynak kodunda görünmezler" bölümü özellikle KONTROL EDİLMEYEN istisnalar için geçerlidir. Bu, kontrol edilen istisnanın bütün noktasıdır ... böylece hangi kodun nereye
atıldığını bilirsiniz

2
@Eva Aynı değiller. Bir goto ifadesi ile gotoanahtar kelimeyi görebilirsiniz . Bir döngü ile kapanış ayracı veya breakveya continueanahtar sözcüğünü görebilirsiniz. Hepsi mevcut yöntemde bir noktaya atlar. Ancak her zaman göremezsiniz throw, çünkü genellikle geçerli yöntemde değil, başka bir yöntemde (muhtemelen dolaylı olarak) çağırır
finnw

5
@finnw İşlevlerin kendileri bir çeşit gotodur. Genellikle, aradığınız işlevlerin hangi işlevleri çağırdığını bilmezsiniz. İşlevler olmadan programladıysanız, görünmez istisnalarla ilgili bir sorununuz olmazdı. Bu, sorunun özel olarak istisnalara bağlı olmadığı ve genel olarak istisnalara karşı geçerli bir argüman olmadığı anlamına gelir. Hata kodlarının daha hızlı olduğunu söyleyebilirsin, monad'ların daha temiz olduğunu söyleyebilirsin, ama goto argümanı aptalca.
Eva

2

İyi, Kontrol Edilmiş İstisnanın gerekli olmadığını kanıtlar:

  1. Bazıları Java için çalışan bir çok çerçeve. JDBC istisnasını denetlenmeyen istisnalara saran ve günlüğe mesaj atan Bahar gibi
  2. Java platformundan sonra bile java'dan sonra gelen birçok dil - onları kullanmıyorlar
  3. İstisnalar işaretlendiğinde, istemcinin bir istisna atan kodu nasıl kullanacağı konusunda kibar bir tahmindir. Ancak bu kodu yazan bir geliştirici, kod istemcisinin çalıştığı sistem ve iş hakkında hiçbir zaman bilemez. Örnek olarak, denetlenen özel durumu atmaya zorlayan arabirim yöntemleri. Sistem üzerinde 100 uygulama vardır, uygulamaların 50 hatta 90'ı bu istisnayı atmaz, ancak kullanıcı o arayüze başvuruyorsa bu istisnayı yakalaması gerekir. Bu 50 veya 90 uygulama, günlüğe istisna koyarak kendi içlerindeki bu istisnaları ele alma eğilimindedir (ve bu onlar için iyi bir davranıştır). Bununla ne yapmalıyız? Tüm bu işi yapacak bazı arka plan mantığım olsa iyi olur - günlüğe mesaj gönderirim. Ve eğer bir kod istemcisi olarak, istisnayı ele almam gerektiğini hissedersem - bunu yapacağım.
  4. Java'da G / Ç ile çalışırken başka bir örnek, dosya yoksa beni tüm istisnaları kontrol etmeye zorlar mı? bununla ne yapmalıyım? Eğer mevcut değilse, sistem bir sonraki adıma geçmez. Bu yöntemin istemcisi, bu dosyadan beklenen içeriği alamaz - Çalışma Zamanı İstisnası'nı işleyebilir, aksi takdirde önce İşaretli İstisna'yı kontrol etmeli, günlüğe bir mesaj koymalı, sonra yöntemden istisna fırlatmalıyım. Hayır ... hayır - RuntimeEception ile otomatik olarak yapsam iyi olur, bunu otomatik olarak yapar. Elle işlemek için herhangi bir anlamı yoktur - Günlükte bir hata mesajı gördüğüm için mutlu olurum (AOP bu konuda yardımcı olabilir .. java'yı düzelten bir şey). Sonunda, sistemin son kullanıcıya pop-up mesajı göstermesi gerektiğini düşünürsem - göstereceğim, bir sorun değil.

Java bir bana temin ederseniz mutluydum seçim I / O gibi çekirdek kütüphanelerini ile çalışırken, kullanmak ne. Like aynı sınıfların iki kopyasını sağlar - biri RuntimeEception ile sarılır. Sonra insanların ne kullanacağını karşılaştırabiliriz . Şimdilik, birçok kişi java veya farklı bir dilde üstte bir çerçeve seçebilirdi. Scala gibi, JRuby her neyse. Birçoğu SUN'un haklı olduğuna inanıyor.


1
Sınıfların iki sürümüne sahip olmak yerine, bir kod bloğu tarafından yapılan yöntem çağrılarının hiçbirinin belirli türden istisnalar atması beklenmiyor ve bu tür istisnaların belirli bir yolla sarılması gerektiğini belirtmek için özlü bir yol olmalı ve yeniden düzenlendi (varsayılan olarak, RuntimeExceptionuygun bir iç istisna ile yeni bir tane oluşturun ). Dış yöntemin iç yönteme throwsbir istisna getirmesinin, ikinci yöntemin daha sık doğru olacağı iç yöntemin istisnalarını sarmaktan daha özlü olması talihsiz bir durumdur.
supercat

2

Kimsenin bahsetmediği önemli bir şey, arayüzlere ve lambda ifadelerine nasıl müdahale ettiği.

Diyelim ki bir MyAppException extends Exception. Uygulamanız tarafından atılan tüm istisnalar tarafından miras alınan üst düzey istisnadır. Her yöntem throws MyAppExceptionhangisinin biraz nükleer, ama yönetilebilir olduğunu açıklar. Özel durum işleyici özel durumu günlüğe kaydeder ve kullanıcıyı bir şekilde bilgilendirir.

Sizinkine ait olmayan bir arayüz uygulamak istediğinize kadar hepsi iyi görünüyor. Açıkçası atma niyetini beyan etmiyor MyApException, bu yüzden derleyici istisnayı oradan atmanıza izin vermiyor.

Ancak, istisnanız genişliyorsa RuntimeException, arabirimlerle ilgili bir sorun olmayacaktır. İsterseniz JavaDoc'taki istisnandan gönüllü olarak söz edebilirsiniz. Ancak bunun dışında, istisna işleme katmanınıza takılmak, sessizce herhangi bir şeyden sessizce.


1

C # 'ın baş mimarı ile ilgili bazı referanslar gördük.

İşte bir Java adamından kontrol edilen istisnaların ne zaman kullanılacağı hakkında alternatif bir bakış açısı. Başkalarının bahsettiği olumsuzlukların çoğunu kabul ediyor: Etkili İstisnalar


2
Java'da denetlenen özel durumlarla ilgili sorun, daha derin bir sorundan kaynaklanır; bu, bir örneğin özelliklerinden ziyade, kural dışı durumun türünde çok fazla bilgi kapsüllenir. "İşaretli" olmak, atma / yakalama sitelerinin bir özelliği ise istisnaları kontrol etmek yararlı olabilir ve kod bloğundan kaçan işaretli bir istisnanın denetlenmiş bir istisna olarak kalması veya görülüp görülmeyeceği bildirilebilirse kontrol edilmeyen bir istisna olarak herhangi bir kapalı blok tarafından; aynı şekilde catch blokları da sadece işaretli istisnalar istediklerini belirleyebilmelidir.
supercat

1
Var olmayan bir anahtara erişilmeye çalışılırsa, belirli bir tür istisna atmak için sözlük arama yordamı belirtildiğini varsayalım. İstemci kodunun böyle bir istisnayı yakalaması makul olabilir. Bununla birlikte, arama rutini tarafından kullanılan bir yöntem, arama rutininin beklemediği bir şekilde aynı türden bir istisnayı atarsa, istemci kodu muhtemelen yakalamamalıdır. Kontrol edilmenin istisna örneklerinin bir özelliği olması , siteleri atması ve siteleri yakalaması, bu tür sorunları önler. İstemci, beklenmedik olanlardan kaçarak bu türden 'denetlenen' istisnaları yakalardı.
supercat

0

İstisna işleme hakkında çok şey okudum, (çoğu zaman) kontrol edilen istisnaların varlığından gerçekten mutlu veya üzgün olduğumu söyleyemem bile, bu benim almam: düşük seviyeli kodda kontrol edilen istisnalar (IO, ağ oluşturma) , OS vb.) Ve yüksek düzey API'larda / uygulama düzeyinde kontrol edilmeyen istisnalar.

Aralarında bir çizgi çizmek o kadar kolay olmasa da, birçok API / kütüphaneyi aynı çatı altına entegre etmenin, her zaman çok fazla kontrol edilen istisnaları sarmadan gerçekten sinir bozucu / zor olduğunu görüyorum, diğer yandan, bazen bazı istisnaları yakalamak ve mevcut bağlamda daha mantıklı olan farklı bir durum sağlamak zorunda kalmak yararlı / daha iyidir.

Üzerinde çalıştığım proje çok sayıda kitaplık alıyor ve bunları tamamen denetlenmeyen istisnalara dayanan aynı API, API altında entegre ediyor.Bu çerçeveler, başlangıçta kontrol edilen istisnalarla dolu ve yalnızca birkaç denetlenmemiş olan üst düzey bir API sağlıyor istisnalar (Başlatma İstisnası, ConfigurationException, vb.) ve çok kolay olmadığını söylemeliyim . Çoğu zaman, nasıl ele alınacağını bilmediğiniz istisnaları yakalamak veya yeniden atmak zorunda kaldınız, hatta umursamıyorsunuz (özellikle karıştırılmamak için istisnaları görmezden gelmelisiniz), özellikle de müşteri tarafında tıklama 10 olası (işaretli) istisna atabilir.

Geçerli sürüm (3. sürüm) yalnızca denetlenmeyen istisnalar kullanır ve yakalanmamış herhangi bir şeyi işlemekten sorumlu bir küresel istisna işleyicisine sahiptir. API, istisna işleyicilerini kaydetmenin bir yolunu sunar; bu, bir istisnanın hata olarak kabul edilip edilmeyeceğine karar verir (çoğu zaman budur), bu da birisine günlüğe kaydet ve bildir anlamına gelir veya başka bir şey anlamına gelebilir - bu istisna, bu da geçerli yürütme iş parçacığını koparmak ve herhangi bir hatayı günlüğe kaydetmemek anlamına gelir. Tabii ki, tüm özel iş parçacığı çözmek için bir deneyin {...} catch (all) ile run () yöntemini işlemek gerekir.

genel void run () {

try {
     ... do something ...
} catch (Throwable throwable) {
     ApplicationContext.getExceptionService().handleException("Handle this exception", throwable);
}

}

Sizin için her şeyi işleyen işleri (Runnable, Callable, Worker) planlamak için WorkerService kullanıyorsanız bu gerekli değildir.

Tabii ki bu sadece benim görüşüm ve doğru olmayabilir, ama bana iyi bir yaklaşım gibi görünüyor. Projeyi serbest bıraktıktan sonra benim için iyi olduğunu düşünüyorum, diğerleri için de iyi olduğunu göreceğim ... :)

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.