Bir IllegalArgumentException ne zaman atılmalıdır?


98

Bunun bir çalışma zamanı istisnası olduğundan endişeleniyorum, bu yüzden muhtemelen idareli kullanılmalıdır.
Standart kullanım durumu:

void setPercentage(int pct) {
    if( pct < 0 || pct > 100) {
         throw new IllegalArgumentException("bad percent");
     }
}

Ancak bu, aşağıdaki tasarımı zorlayacak gibi görünüyor:

public void computeScore() throws MyPackageException {
      try {
          setPercentage(userInputPercent);
      }
      catch(IllegalArgumentException exc){
           throw new MyPackageException(exc);
      }
 }

Kontrol edilen bir istisna haline getirmek için.

Tamam, ama bununla gidelim. Yanlış girdi verirseniz, çalışma zamanı hatası alırsınız. İlk olarak, bu aslında tek tip olarak uygulanması oldukça zor bir politika, çünkü tam tersi bir dönüşümü yapmanız gerekebilir:

public void scanEmail(String emailStr, InputStream mime) {
    try {
        EmailAddress parsedAddress = EmailUtil.parse(emailStr);
    }
    catch(ParseException exc){
        throw new IllegalArgumentException("bad email", exc);
    }
}

Daha da kötüsü - 0 <= pct && pct <= 100müşteri kodunu kontrol etmenin statik olarak yapılması beklenebilirken, bu, bir e-posta adresi gibi daha gelişmiş veriler için geçerli değildir veya daha kötüsü, bir veritabanında kontrol edilmesi gereken bir şeydir, bu nedenle genel olarak müşteri kodu önceden olamaz doğrulayın.

Yani temelde söylemek istediğim, kullanımı için anlamlı ve tutarlı bir politika görmüyorum IllegalArgumentException. Görünüşe göre kullanılmamalı ve kendi kontrol edilen istisnalarımıza bağlı kalmalıyız. Bunu atmak için iyi bir kullanım durumu nedir?

Yanıtlar:


81

API dokümanı IllegalArgumentException:

Bir yöntemin geçersiz veya uygunsuz bir argüman geçirildiğini belirtmek için atılır.

JDK kitaplıklarında nasıl kullanıldığına bakarak şunu söyleyebilirim:

  • Girdi işe girmeden ve anlamsız bir hata mesajıyla bir şeyin başarısız olmasına neden olmadan önce açıkça kötü girdiden şikayet etmek savunmaya yönelik bir önlem gibi görünüyor.

  • Kontrol edilen bir istisna atmanın çok can sıkıcı olacağı durumlar için kullanılır (java.lang.reflect kodunda, kontrol edilen istisna atmanın gülünç seviyeleriyle ilgili endişelerin başka türlü belirgin olmadığı durumlarda).

Ben kullanırsınız IllegalArgumentException(JDK kullanımı ile tutarlı kalmaya çalışan) ortak programlar için son dakika savunma argümanı denetimi yapmak. Veya beklentinin kötü bir argümanın bir programcı hatası olması olduğu durumlarda NullPointerException,. İş kodunda doğrulama uygulamak için kullanmazdım. E-posta örneği için kesinlikle kullanmazdım.


8
"Beklentinin kötü bir argümanın bir programcı hatası olduğu" tavsiyesinin en çok bunun nasıl kullanıldığını gördüğümle tutarlı olduğunu düşünüyorum, bu yüzden bu cevabı kabul etmek.
djechlin

23

"Hatalı girdi" den bahsederken, girdinin nereden geldiğini düşünmelisiniz.

Giriş bir kullanıcı tarafından mı yoksa kontrol etmediğiniz başka bir harici sistem tarafından mı girilmişse, girişin geçersiz olmasını beklemeli ve her zaman doğrulamalısınız. Bu durumda kontrol edilmiş bir istisna atmanız tamamen uygundur. Uygulamanız, kullanıcıya bir hata mesajı göndererek bu istisnadan 'kurtulmalıdır'.

Girdi, kendi sisteminizden, örneğin veritabanınızdan veya uygulamanızın diğer bölümlerinden geliyorsa, geçerli olması için ona güvenebilmelisiniz (oraya ulaşmadan önce doğrulanmış olması gerekir). Bu durumda, yakalanmaması gereken bir IllegalArgumentException gibi denetlenmemiş bir istisna atmak tamamen uygundur (genel olarak denetlenmemiş istisnaları asla yakalamamalısınız). İlk etapta geçersiz değerin oraya gelmesi bir programcının hatasıdır;) Bunu düzeltmeniz gerekir.


2
Neden "kontrol edilmeyen istisnaları asla yakalamamalısınız"?
Koray Tugay

9
Çünkü kontrol edilmeyen bir istisna, bir programlama hatası sonucu fırlatılmak içindir. Bu tür istisnaları atan bir yöntemi arayanın, makul bir şekilde ondan kurtarması beklenemez ve bu nedenle, tipik olarak onları yakalamak mantıklı değildir.
Tom

1
Because an unchecked exception is meant to be thrown as a result of a programming errorkafamdaki birçok şeyi temizlememe yardımcı oldu, teşekkür ederim :)
svarog

15

Çalışma zamanı istisnalarını "idareli" fırlatmak gerçekten iyi bir ilke değildir - Etkili Java , arayanın makul bir şekilde kurtarmasının beklendiği durumlarda , kontrol edilen istisnaları kullanmanızı önerir . (Programcı hatası spesifik bir örnektir: belirli bir durum programcı hatasını gösteriyorsa, kontrol edilmeyen bir istisna atmalısınız; programcının mantık sorununun meydana geldiği yerin bir yığın izine sahip olmasını istersiniz, bunu kendiniz halletmeye çalışmasın.)

İyileşme umudu yoksa, kontrol edilmemiş istisnaları kullanmaktan çekinmeyin; onları yakalamanın bir anlamı yok, bu yüzden gayet iyi.

Yine de bu örneğin kodunuzda hangi durumda olduğu örneğinizden% 100 net değil.


Bence "makul bir şekilde iyileşmesi bekleniyor" sinsi. Arayanın, bazı veriler hatalı biçimlendirilmiş olsa bile, mümkün olduğunca çok kişinin başarılı olmasını isteyebileceği herhangi bir işlem gerçekleşmiş foo(data)olabilir for(Data data : list) foo(data);. Programla ilgili hataları da içerir, eğer uygulamamın başarısız olması bir işlemin gerçekleşmeyeceği anlamına geliyorsa, bu muhtemelen daha iyi, nükleer soğutmanın çevrimdışı olduğu anlamına geliyorsa bu kötü.
djechlin

StackOverflowErrorve bu tür durumlar, arayanın makul olarak iyileşmesinin beklenemeyeceği durumlardır. Ancak, herhangi bir veri veya uygulama mantık seviyesi durumunun kontrol edilmesi gerektiği gibi geliyor. Bu, boş işaretçi kontrollerinizi yapın anlamına gelir!
djechlin

4
Bir nükleer soğutma uygulamasında, programcının fark edilmeden geçmesinin imkansız olduğunu düşündüğü bir duruma izin vermektense, testlerde çok başarısız olmayı tercih ederim.
Louis Wasserman

Boolean.parseBoolean (..), "arayanın makul olarak kurtarması beklense de" IllegalArugmentException oluşturur. bu yüzden ..... onu idare etmek veya arayana geri dönmek kodunuza bağlıdır.
Jeryl Cook

6

Oracle resmi eğitiminde belirtildiği gibi, şunları belirtir:

Bir istemcinin bir istisnadan makul olarak kurtarması bekleniyorsa, bunu kontrol edilmiş bir istisna yapın. İstemci istisnadan kurtarmak için hiçbir şey yapamazsa, bunu kontrol edilmeyen bir istisna yapın.

Kullanarak veritabanıyla etkileşim kuran bir Uygulamam varsa JDBC, Ve argümanı int itemve olarak alan bir yöntemim var double price. Karşılık pricegelen öğe için veritabanı tablosundan okunur. itemSatın alınan toplam sayıyı pricedeğerle çarpıp sonucu döndürüyorum. Tablodaki fiyat alanı değerinin asla negatif olamayacağından her zaman emin olsam da (Uygulama tarafı). Peki ya fiyat değeri negatif çıkarsa ? Veritabanı tarafında ciddi bir sorun olduğunu gösteriyor. Operatör tarafından yanlış fiyat girişi olabilir. Bu, o yöntemi çağıran uygulamanın diğer kısmının tahmin edemediği ve ondan kurtaramadığı türden bir sorundur. Bu bir olduğunu BUGveritabanınızda. Yani veIllegalArguementException()bunu ifade edecek bu durumda atılması gerekir the price can't be negative.
Umarım düşüncemi açıkça ifade etmişimdir ..


Bu (oracle'ın) tavsiyesinden hoşlanmıyorum çünkü istisna halinin nasıl iyileşeceği, iyileşip iyileşmeyeceği ile ilgilidir. Örneğin, hatalı biçimlendirilmiş bir kullanıcı isteği, tüm web sunucusunu çökertmeye değmez.
djechlin

6

Herhangi bir API, herhangi bir genel yöntemin her parametresinin geçerliliğini çalıştırmadan önce kontrol etmelidir:

void setPercentage(int pct, AnObject object) {
    if( pct < 0 || pct > 100) {
        throw new IllegalArgumentException("pct has an invalid value");
    }
    if (object == null) {
        throw new IllegalArgumentException("object is null");
    }
}

Uygulamadaki zaman hatalarının% 99,9'unu temsil ediyorlar çünkü imkansız işlemler istiyorlar, bu yüzden sonunda uygulamayı çökertmesi gereken hatalar oluyorlar (bu nedenle kurtarılamaz bir hatadır).

Bu durumda ve hızlı başarısızlık yaklaşımını takip ederek, uygulama durumunu bozmamak için uygulamanın bitmesine izin vermelisiniz.


Bir API istemcisi bana kötü girdi verirse Aksine, ben gerektiği değil benim tüm API sunucusuna çökmesine.
djechlin

2
Tabii ki, API sunucunuzu çökertmemeli, ancak arayana bir istisna döndürmelidir. Bu, istemciden başka hiçbir şeyi çökertmemelidir.
Ignacio Soler Garcia

Yorumda yazdıklarınız cevapta yazdığınız şey değil.
djechlin

1
Açıklayayım, API'ye yanlış parametrelerle (bir hata) yapılan çağrı bir üçüncü taraf istemci tarafından yapılırsa, istemcinin çökmesi gerekir. Yöntemi yanlış parametrelerle çağıran bir hataya sahip olan API sunucusu ise, API sunucusu çökmelidir. Kontrol edin: en.wikipedia.org/wiki/Fail-fast
Ignacio Soler Garcia

3

Treat IllegalArgumentExceptionbir şekilde ön koşullardan kontrol ve tasarım prensibi göz önünde bulundurun: Bir kamu yöntem hem bilgi ve kamuya kendi ön şartları belgelemek gerekir.

Bu örneğin doğru olduğuna katılıyorum:

void setPercentage(int pct) {
    if( pct < 0 || pct > 100) {
         throw new IllegalArgumentException("bad percent");
     }
}

EmailUtil opak ise , yani ön koşulların son kullanıcıya açıklanamamasının bir nedeni varsa, o zaman kontrol edilen bir istisna doğrudur. Bu tasarım için düzeltilen ikinci versiyon:

import com.someoneelse.EmailUtil;

public void scanEmail(String emailStr, InputStream mime) throws ParseException {
    EmailAddress parsedAddress = EmailUtil.parseAddress(emailStr);
}

EmailUtil şeffafsa, örneğin söz konusu sınıfa ait özel bir yöntem olabilir, IllegalArgumentExceptionancak ve ancak ön koşulları işlev belgelerinde açıklanabiliyorsa doğrudur. Bu aynı zamanda doğru bir versiyondur:

/** @param String email An email with an address in the form abc@xyz.com
 * with no nested comments, periods or other nonsense.
 */
public String scanEmail(String email)
  if (!addressIsProperlyFormatted(email)) {
      throw new IllegalArgumentException("invalid address");
  }
  return parseEmail(emailAddr);
}
private String parseEmail(String emailS) {
  // Assumes email is valid
  boolean parsesJustFine = true;
  // Parse logic
  if (!parsesJustFine) {
    // As a private method it is an internal error if address is improperly
    // formatted. This is an internal error to the class implementation.
    throw new AssertError("Internal error");
  }
}

Bu tasarım her iki şekilde de gidebilir.

  • Ön koşulların tanımlanması pahalıysa veya sınıf, e-postalarının geçerli olup olmadığını bilmeyen müşteriler tarafından kullanılmak üzere tasarlanmışsa, o zaman kullanın ParseException. Buradaki en üst düzey yöntem scanEmail, son kullanıcının çalışılmamış e-posta göndermeyi planladığını ve bu nedenle bu muhtemelen doğru olduğunu ima eden adlandırılmıştır .
  • Ön koşullar işlev belgelerinde açıklanabiliyorsa ve sınıf geçersiz giriş niyetinde değilse ve bu nedenle programcı hatası belirtilmişse, kullanın IllegalArgumentException. "İşaretlenmemiş" olmasına rağmen "kontrol", müşterinin uyması beklenen işlevi belgeleyen Javadoc'a geçer. IllegalArgumentExceptionMüşterinin argümanının yasa dışı olduğunu önceden söyleyememesi yanlıştır.

IllegalStateException hakkında bir not : Bu, "bu nesnenin dahili durumu (özel örnek değişkenleri) bu eylemi gerçekleştiremiyor" anlamına gelir. Son kullanıcı, özel durumu bu kadar gevşek bir şekilde göremez IllegalArgumentException, istemci çağrısının nesnenin durumunun tutarsız olduğunu bilmesinin bir yolu olmadığı durumda öncelik kazanır. Kontrol edilen istisnalara tercih edildiğinde iyi bir açıklamam yok, ancak iki kez başlatma veya kurtarılmayan bir veritabanı bağlantısını kaybetme gibi şeyler örnek.

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.