Java assert anahtar sözcüğü ne yapar ve ne zaman kullanılmalıdır?


601

İddiaların kilit rolünü anlamak için bazı gerçek hayat örnekleri nelerdir?


8
Gerçek hayatta neredeyse hiç görmezsiniz. Konjektif: Eğer iddiaları kullanırsanız, üç durum hakkında düşünmeniz gerekir: Onay pasoları, iddia başarısız olur, iddia iki değil, kapalıdır. Ve assert varsayılan olarak kapalıdır, bu en olası durumdur ve kodunuz için etkinleştirildiğinden emin olmak zordur. Bu, eklentilerin sınırlı kullanımda olabilecek bir erken optimizasyon olduğudur. @ Bjorn'un cevabında gördüğünüz gibi, her zaman bir iddiada başarısız olmak istemeyeceğiniz bir kullanım durumu bulmak bile zordur.
Yishai

35
@Yishai: " Düşünmeniz gerekiyor ... iddia kapalı" Bunu yapmanız gerekiyorsa, yanlış yapıyorsunuz. "iddialar sınırlı kullanım erken bir optimizasyon vardır" Bu hemen hemen raydan. Sun'ın bunu ele alacağı şey: " Java Teknolojisinde İddiaları Kullanma " ve bunun da okunması iyi: " İddialarla programlamanın faydaları (diğer iddialar olarak da ifade edilir) "
David Tonhofer

5
@DavidTonhofer, gerçek hayatta neredeyse hiç görmezsiniz. Bu doğrulanabilir. İstediğiniz kadar açık kaynaklı projeyi kontrol edin. Değişmezleri doğrulamadığınızı söylemiyorum. Bu aynı şey değil. Başka bir yol dene. Ekler çok önemliyse, neden varsayılan olarak kapalıdır?
Yishai

17
Bir referans, FWIW: Yazılım iddiaları ile kod kalitesi arasındaki ilişki : "Ayrıca iddiaların kaynak kodu statik analiz araçları gibi popüler hata bulma teknikleriyle olan etkinliğini karşılaştırıyoruz. Olgu çalışmamızdan iddia yoğunluğunda artış olduğunu gözlemliyoruz. bir dosyada hata yoğunluğunda istatistiksel olarak anlamlı bir azalma var. "
David Tonhofer

4
@DavidTonhofer David, iddiaya olan sevginizin yaptığınız çok özel bir programlama türü olduğunu düşünüyorum, alanımda HERHANGİ BİR sebepten dolayı programdan çıkan web uygulamalarıyla çalışan en büyük HAYIR HAYIR - kişisel olarak hiç birim /
integ

Yanıtlar:


426

Java 1.4'e iddialar ( assert anahtar sözcüğü aracılığıyla) eklendi. Koddaki bir değişmezin doğruluğunu doğrulamak için kullanılırlar. Hiçbir zaman üretim kodunda tetiklenmemelidirler ve bir kod yolunda bir hata veya yanlış kullanım olduğunu gösterirler. Komuttaki -easeçenek aracılığıyla çalışma zamanında etkinleştirilebilirler java, ancak varsayılan olarak açılmazlar.

Bir örnek:

public Foo acquireFoo(int id) {
  Foo result = null;
  if (id > 50) {
    result = fooService.read(id);
  } else {
    result = new Foo(id);
  }
  assert result != null;

  return result;
}

71
Aslında, Oracle assertherkese açık yöntem parametrelerini ( docs.oracle.com/javase/1.4.2/docs/guide/lang/assert.html ) kontrol etmek için kullanmamayı söyler . Bu bir Exceptionprogramı öldürmek yerine atmalıdır.
SJuan76

10
Ama hala neden var olduklarını açıklamıyorsunuz. Neden if () kontrolü yapıp bir istisna alamıyorsunuz?
El Mac

7
@ElMac - iddialar döngünün geliştirici / hata ayıklama / test bölümleri içindir - üretim için değildir. Bir if bloğu ürün içinde çalışır. Basit iddialar bankayı kırmaz, ancak karmaşık veri doğrulaması yapan pahalı iddialar üretim ortamınızı düşürebilir, bu yüzden orada kapatılırlar.
hoodaticus

2
@hoodaticus sadece prod kod için tüm iddiaları açıp kapatabilirsiniz gerçeği demek? Çünkü karmaşık veri doğrulamayı yine de yapabilirim ve istisnalar dışında işleyebilirim. Üretim kodum varsa, karmaşık (ve belki de pahalı) iddiaları kapatabilirim, çünkü çalışmalı ve zaten test edilmiş mi? Teorik olarak programı düşürmemeliler çünkü o zaman zaten bir probleminiz olacaktı.
El Mac

8
This convention is unaffected by the addition of the assert construct. Do not use assertions to check the parameters of a public method. An assert is inappropriate because the method guarantees that it will always enforce the argument checks. It must check its arguments whether or not assertions are enabled. Further, the assert construct does not throw an exception of the specified type. It can throw only an AssertionError. docs.oracle.com/javase/8/docs/technotes/guides/language/…
Bakhshi

325

Bir nükleer enerji santralini kontrol etmek için bir program yazmanız gerektiğini varsayalım. En küçük hatanın bile yıkıcı sonuçlara sahip olabileceği oldukça açıktır, bu nedenle kodunuzun hatasız olması gerekir (JVM'nin argüman uğruna hatasız olduğu varsayılarak).

Java, doğrulanabilir bir dil değildir, yani: işleminizin sonucunun mükemmel olacağını hesaplayamazsınız. Bunun ana nedeni işaretçilerdir: herhangi bir yere veya hiçbir yere işaret edemezler, bu nedenle en azından makul bir kod aralığı dahilinde değil, bu tam değere sahip oldukları hesaplanamazlar. Bu sorun göz önüne alındığında, kodunuzun bir bütün olarak doğru olduğunu kanıtlamanın bir yolu yoktur. Ancak yapabileceğiniz şey, en azından her hatayı oluştuğunda bulduğunuzu kanıtlamaktır.

Bu fikir, Sözleşmeye Göre Tasarım (DbC) paradigmasına dayanır: önce yönteminizin ne yapması gerektiğini (matematiksel hassasiyetle) tanımlarsınız ve daha sonra bunu gerçek yürütme sırasında test ederek doğrularsınız. Misal:

// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
  return a + b;
}

Bu iyi çalışmak oldukça açık olsa da, çoğu programcı bunun içindeki gizli hatayı görmeyecektir (ipucu: Ariane V benzer bir hata nedeniyle çöktü). Şimdi DbC, düzgün çalıştığını doğrulamak için her zaman bir işlevin giriş ve çıkışını kontrol etmeniz gerektiğini tanımlar . Java bunu iddialarla yapabilir:

// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
    assert (Integer.MAX_VALUE - a >= b) : "Value of " + a + " + " + b + " is too large to add.";
  final int result = a + b;
    assert (result - a == b) : "Sum of " + a + " + " + b + " returned wrong sum " + result;
  return result;
}

Bu işlev şimdi başarısız olursa, fark edeceksiniz. Kodunuzda bir sorun olduğunu bilirsiniz, nerede olduğunu bilirsiniz ve neye neden olduğunu bilirsiniz (İstisnalara benzer). Ve daha da önemlisi: başka bir kodun yanlış değerlerle çalışmasını önlemek ve kontrol ettiği her şeye zarar vermesini önlemek için doğru yürütmeyi durdurursunuz.

Java İstisnaları benzer bir kavramdır, ancak her şeyi doğrulayamazlar. Daha fazla kontrol istiyorsanız (yürütme hızı pahasına) iddiaları kullanmanız gerekir. Bunu yapmak kodunuzu şişirir, ancak sonunda bir ürünü şaşırtıcı derecede kısa bir geliştirme zamanında teslim edebilirsiniz (ne kadar erken bir hatayı düzeltirseniz, maliyet o kadar düşük olur). Ayrıca: kodunuzun içinde herhangi bir hata varsa, onu tespit edersiniz. Bir hatanın kayması ve daha sonra sorunlara neden olmasının bir yolu yoktur.

Bu hala hatasız kod için bir garanti değildir, ancak normal programlardan çok daha yakındır.


29
Görünüşe göre hatasız kodda gizli hatalar sunduğu için bu örneği seçtim. Bu, başka birinin sunduğuna benziyorsa, belki de akılda aynı fikir vardı. ;)
İki:

8
İddiayı seçersiniz çünkü iddianın yanlış olması başarısız olur. Bir if herhangi bir davranışa sahip olabilir. Saçak davalarını vurmak Birim Testinin görevidir. Sözleşme ile Tasarım'ı kullanmak sözleşmeyi oldukça iyi tanımlamıştır, ancak gerçek hayat sözleşmelerinde olduğu gibi, bunlara saygı duyulduğundan emin olmak için bir kontrole ihtiyacınız vardır. İddianameler ile sözleşmeye saygısızlık edildiğinde sizi bekleyen bir bekçi köpeği takılır. Bunu, imzaladığınız bir sözleşmenin dışında veya aleyhinde her şeyi yaptığınızda "YANLIŞ" diye çığlık atan bir avukat olarak düşünün ve daha sonra çalışmaya devam edip sözleşmeyi daha fazla ihlal edemezsiniz!
Eric

5
Bu basit durumda gerekli: hayır, ancak DbC her sonucun kontrol edilmesi gerektiğini tanımlar . Birisinin şimdi bu işlevi çok daha karmaşık bir şeye değiştirdiğini düşünün, sonra post-check'i de uyarlaması gerekiyor ve sonra aniden faydalı oluyor.
TwoThe

4
Bunu dirilttiğim için üzgünüm, ama özel bir sorum var. @TwoThe'un yaptığı ve assert'i sadece a new IllegalArgumentExceptionile mesaj atmak yerine ne arasındaki fark nedir ? Yani, o throwsyöntem bildirimi ve bu istisnayı başka bir yerde yönetmek için kod eklemek o bir yana . Neden assertyeni bir İstisna atmak istiyorsun? Ya da ifbunun yerine neden olmasın assert? Gerçekten
bulamıyorum

14
-1: Taşma olup olmadığını kontrol etme iddiası anegatif olabilirse yanlıştır . İkinci iddia işe yaramaz; int değerleri için her zaman a + b - b == a söz konusudur. Bu sınama yalnızca bilgisayar temelden kırıksa başarısız olabilir. Bu olasılığa karşı savunmak için, birden fazla CPU arasında tutarlılığı kontrol etmeniz gerekir.
kevin cline

63

İddialar, kodunuzdaki hataları yakalamak için bir geliştirme aşaması aracıdır. Kolayca çıkarılacak şekilde tasarlanmıştır, bu nedenle üretim kodunda mevcut olmazlar. Dolayısıyla, iddialar müşteriye sunduğunuz "çözüm" ün bir parçası değildir. Yaptığınız varsayımların doğru olduğundan emin olmak için dahili kontrollerdir. En yaygın örnek null değerini sınamaktır. Birçok yöntem şöyle yazılır:

void doSomething(Widget widget) {
  if (widget != null) {
    widget.someMethod(); // ...
    ... // do more stuff with this widget
  }
}

Çoğu zaman böyle bir yöntemde, widget hiçbir zaman boş olmamalıdır. Bu nedenle, boşsa, kodunuzda izlemeniz gereken bir hata vardır. Ancak yukarıdaki kod size bunu asla söylemeyecektir. Yani "güvenli" kod yazmak için iyi niyetli bir çabayla, bir hatayı da saklıyorsunuz. Bunun gibi bir kod yazmak çok daha iyidir:

/**
 * @param Widget widget Should never be null
 */
void doSomething(Widget widget) {
  assert widget != null;
  widget.someMethod(); // ...
    ... // do more stuff with this widget
}

Bu şekilde, bu hatayı erken yakalayacağınızdan emin olacaksınız. (Sözleşmede bu parametrenin hiçbir zaman boş olmaması gerektiğini belirtmek de yararlıdır.) Geliştirme sırasında kodunuzu test ederken iddiaları açtığınızdan emin olun. (Ve meslektaşlarınızı da bunu yapmaya ikna etmek genellikle zordur, ki bu çok can sıkıcı buluyorum.)

Şimdi, bazı meslektaşlarınız, üretimde bir istisnayı önlemek için yine de boş kontrol yapmanız gerektiğini savunarak bu koda itiraz edecekler. Bu durumda, iddia hala yararlıdır. Bunu şöyle yazabilirsiniz:

void doSomething(Widget widget) {
  assert widget != null;
  if (widget != null) {
    widget.someMethod(); // ...
    ... // do more stuff with this widget
  }
}

Bu şekilde, iş arkadaşlarınız null kontrolünün üretim kodu için orada olduğu için mutlu olacaklar, ancak geliştirme sırasında artık widget null olduğunda hatayı gizlemiyorsunuz.

İşte gerçek dünya örneği: Bir keresinde eşitlik için iki keyfi değeri karşılaştıran bir yöntem yazdım, burada her iki değer de boş olabilir:

/**
 * Compare two values using equals(), after checking for null.
 * @param thisValue (may be null)
 * @param otherValue (may be null)
 * @return True if they are both null or if equals() returns true
 */
public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
  } else {
    result = thisValue.equals(otherValue);
  }
  return result;
}

Bu kod, equals()thisValue değerinin boş olmaması durumunda yöntemin çalışmasını devreder. Ancak, null parametresini uygun equals()şekilde işleyerek yöntemin sözleşmeyi doğru şekilde yerine getirdiğini varsayar equals().

Bir meslektaşım, kodumuza itiraz etti ve sınıflarımızın çoğunun equals()null için test edilmeyen buggy yöntemleri olduğunu söyledi, bu yüzden bu kontrolü bu yönteme koymalıyım. Bu akıllıca ise tartışmalı veya hatayı zorlamalıyız, bu yüzden onu tespit edip düzeltebiliriz, ancak meslektaşım için ertelendi ve bir yorum ile işaretlediğim boş bir kontrol yaptım:

public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
  } else {
    result = otherValue != null && thisValue.equals(otherValue); // questionable null check
  }
  return result;
}

Buradaki ek kontrol other != null, ancak equals()yöntem, sözleşmesinin gerektirdiği şekilde null olup olmadığını kontrol edemezse gereklidir.

İş arkadaşımla, buggy kodunun kod tabanımızda kalmasına izin vermenin bilgeliği hakkında meyvesiz bir tartışmaya girmek yerine, sadece kodda iki iddiada bulundum. Bu iddialar, geliştirme aşamalarında, sınıflarımızdan biri equals()düzgün bir şekilde uygulanamazsa, bunu bana bildirir , böylece düzeltebilirim:

public static boolean compare(final Object thisValue, final Object otherValue) {
  boolean result;
  if (thisValue == null) {
    result = otherValue == null;
    assert otherValue == null || otherValue.equals(null) == false;
  } else {
    result = otherValue != null && thisValue.equals(otherValue);
    assert thisValue.equals(null) == false;
  }
  return result;
}

Akılda tutulması gereken önemli noktalar şunlardır:

  1. İddialar sadece geliştirme aşaması araçlarıdır.

  2. Bir iddianın amacı, sadece kodunuzda değil, kod tabanınızda bir hata olup olmadığını bildirmektir . (Buradaki iddialar aslında diğer sınıflardaki hataları işaretleyecektir.)

  3. Meslektaşım sınıflarımızın düzgün bir şekilde yazıldığından emin olsa bile, buradaki iddialar hala yararlı olacaktır. Null değerini sınamayabilecek yeni sınıflar eklenir ve bu yöntem bu hataları bizim için işaretleyebilir.

  4. Geliştirmede, yazdığınız kod iddia kullanmasa bile her zaman iddiaları açmalısınız. IDE'm bunu yeni yürütülebilir dosyalar için her zaman varsayılan olarak yapacak şekilde ayarlanmıştır.

  5. İddialar, üretimdeki kodun davranışını değiştirmez, bu nedenle meslektaşım, null kontrolün orada olduğundan ve equals()yöntem buggy olsa bile bu yöntemin düzgün bir şekilde yürütülmesinden memnun olur . Mutluyum çünkü equals()kalkınmada herhangi bir buggy yöntemi yakalayacağım .

Ayrıca, başarısız olacak geçici bir iddiayı koyarak onaylama politikanızı test etmelisiniz, böylece günlük dosyası veya çıktı akışındaki bir yığın izlemesi yoluyla bilgilendirileceğinizden emin olabilirsiniz.


"Bir hata gizleme" ve geliştirme sırasında hatalar ortaya nasıl iddia iyi noktalar!
nobar

Bu kontrollerin hiçbiri yavaş değildir, bu nedenle üretimde kapatmak için bir neden yoktur. Bunlar, "geliştirme aşaması" nda görünmeyen sorunları tespit edebilmeniz için günlük ifadelerine dönüştürülmelidir. (Gerçekten de, geliştirme aşaması diye bir şey yoktur, zaten kodunuzu korumayı bırakmaya karar verdiğinizde geliştirme sona erer.)
Aleksandr Dubinsky

20

assertAnahtar kelimenin ne yaptığını açıklayan çok sayıda iyi yanıt , ancak asıl soruyu yanıtlayan çok az cevap var, "assert anahtar kelime gerçek hayatta kullanılmalıdır?"

Cevap: neredeyse asla .

Kavram olarak iddialar harikadır. İyi kod çok sayıda if (...) throw ...ifadeye sahiptir (ve akrabaları Objects.requireNonNullve gibi Math.addExact). Bununla birlikte, belirli tasarım kararları, assert anahtar kelimenin kendisinin kullanımını büyük ölçüde sınırlandırmıştır .

assertAnahtar kelimenin arkasındaki itici fikir erken optimizasyon ve ana özellik tüm kontrolleri kolayca kapatabiliyor. Aslında, assertkontroller varsayılan olarak kapalıdır.

Ancak üretimde değişmez kontrollerin yapılmaya devam edilmesi kritik önem taşımaktadır. Bunun nedeni, mükemmel test kapsamının imkansız olması ve tüm üretim kodlarının, teşhislerin teşhis edilmesine ve hafifletilmesine yardımcı olması gereken hatalara sahip olmasıdır.

Bu nedenle, if (...) throw ...genel yöntemlerin parametre değerlerini kontrol etmek ve atmak için gerekli olduğu gibi , kullanımı tercih edilmelidir IllegalArgumentException.

Bazen, işlenmesi istenmeyen uzun bir zaman alan (ve önemli olması için yeterince sık çağrılan) değişmez bir kontrol yazmak cazip gelebilir. Bununla birlikte, bu tür kontroller istenmeyen bir testtir. Bu tür zaman alıcı kontroller genellikle birim testler olarak yazılır. Bununla birlikte, bazen assertbu nedenle kullanmak mantıklı olabilir .

Kullanmayın asserto daha temiz ve daha güzel olduğu için basitçe if (...) throw ...(ve çünkü ben temiz ve hoş gibi büyük bir acıyla söylemek). Kendinize yardım edemiyorsanız ve uygulamanızın nasıl başlatıldığını kontrol edebiliyorsanız, kullanmaktan çekinmeyin, assertancak üretimde her zaman iddiaları etkinleştirin. Kuşkusuz, bunu yapmaya eğilimliyim. Daha çok asserthareket etmesine neden olacak bir lombok ek açıklaması için bastırıyorum if (...) throw .... Burada oy verin.

(Rant: JVM geliştiricileri, bir sürü korkunç, erken optimizasyon kodlayıcıydı. Bu yüzden Java eklentisinde ve JVM'de çok fazla güvenlik sorunu duydunuz. Üretim koduna temel kontrolleri ve iddiaları eklemeyi reddettik ve fiyatı ödeyin.)


2
@aberglas Bir catch-all yan tümcesi catch (Throwable t). OutOfMemoryError, AssertionError, vs.'den tuzak kurmaya, günlüğe kaydetmeye veya yeniden denemeye / kurtarmaya çalışmamak için hiçbir neden yoktur
Aleksandr Dubinsky

1
OutOfMemoryError'ı yakaladım ve kurtardım.
MiguelMunoz

1
Kabul etmiyorum. İddialarımın çoğu, API'mın doğru şekilde çağrıldığından emin olmak için kullanılıyor. Örneğin, yalnızca bir nesne kilit tuttuğunda çağrılması gereken özel bir yöntem yazabilirim. Başka bir geliştirici bu yöntemi kodun nesneyi kilitlemeyen kısmından çağırırsa, iddia onlara hemen bir hata yaptıklarını söyler. Bunun gibi, kesinlikle, geliştirme aşamasında yakalanabilen birçok hata var ve bu durumlarda iddialar çok yararlı.
MiguelMunoz

2
@MiguelMunoz Cevabımda iddia fikrinin çok iyi olduğunu söyledim. assertAnahtar kelimenin uygulanması kötüdür. Cevaba değil, anahtar kelimeye atıfta bulunduğumu daha açık hale getirmek için cevabımı düzenleyeceğim.
Aleksandr Dubinsky

2
Bir istisna yerine bir AssertionError atıyor gibi. Çok fazla geliştirici, kod yalnızca IOException gibi bir şey atabiliyorsa, İstisna yakalamaması gerektiğini hala öğrenememiştir. Birisi İstisna yakaladı çünkü kodumda hatalar tamamen yuttu aldım. Bu tuzağa iddialarda bulunulmuyor. İstisnalar, üretim kodunda görmeyi beklediğiniz durumlar içindir. Günlük kaydı ile ilgili olarak, hatalar nadir de olsa tüm hatalarınızı da günlüğe kaydetmelisiniz. Örneğin, bir OutOfMemoryError öğesinin günlüğe kaydetmeden geçmesine gerçekten izin vermek istiyor musunuz?
MiguelMunoz

14

İşte en yaygın kullanım örneği. Bir numaralandırma değerini açtığınızı varsayalım:

switch (fruit) {
  case apple:
    // do something
    break;
  case pear:
    // do something
    break;
  case banana:
    // do something
    break;
}

Her davayı ele aldığınız sürece, iyisiniz. Ama bir gün, biri enumunuza incir ekleyecek ve bunu anahtar ifadenize eklemeyi unutacak. Bu, yakalanması zor olabilecek bir hata üretir, çünkü efektler, switch deyimini terk edene kadar hissedilmez. Ancak anahtarınızı böyle yazarsanız, hemen yakalayabilirsiniz:

switch (fruit) {
  case apple:
    // do something
    break;
  case pear:
    // do something
    break;
  case banana:
    // do something
    break;
  default:
    assert false : "Missing enum value: " + fruit;
}

4
Bu yüzden uyarıları etkinleştirmeniz ve uyarılara hata olarak davranmanız gerekir. Herhangi bir yarı iyi derleyici, size söylemesine izin verirseniz, bir numaralandırma kontrolünün eksik olduğunu söyleyebilir ve bunu derleme zamanında yapar, ki bu da (belki bir gün) Çalışma süresi.
Mike Nakis

9
neden bir tür istisna yerine bir iddiayı kullanıyorsunuz, örneğin bir illegalargumentexception?
liltitus27

4
Bu, AssertionErrorif ifadeleri etkinleştirilmişse ( -ea) atar . Üretimde istenen davranış nedir? Daha sonra infaz sessiz sessiz bir op ve potansiyel felaket? Muhtemelen değil. Ben açık bir öneriyorum throw new AssertionError("Missing enum value: " + fruit);.
aioobe

1
Sadece bir AssertionError hatası atmak için iyi bir argüman var. Üretimde doğru davranışa gelince, iddiaların bütün amacı bunun üretimde olmasını engellemektir. İddialar, üretim kodundan kolayca çıkarılabilen hataları yakalamak için bir geliştirme aşaması aracıdır. Bu durumda, üretim kodundan kaldırmak için bir neden yoktur. Ancak birçok durumda, bütünlük testleri işleri yavaşlatabilir. Bu testi üretim kodunda kullanılmayan iddiaların içine koyarak, üretim kodunuzu yavaşlatacaklarından endişe etmeden kapsamlı testler yazabilirsiniz.
MiguelMunoz

Bu yanlış görünüyor. IMHO kullanmamalısınız defaultki derleyici kayıp durumlarda sizi uyarabilir. Bunun returnyerine break(yöntemin çıkarılması gerekebilir) ve daha sonra anahtardan sonra eksik durumu işleyebilirsiniz. Bu şekilde hem uyarı hem de bir fırsat elde edersiniz assert.
maaartinus

12

Son koşullar ve "asla başarısız olmamalıdır" ön koşullarını kontrol etmek için iddialar kullanılır. Doğru kod asla bir iddiada başarısız olmamalıdır; tetiklediklerinde bir hata belirtmelidirler (umarım sorunun gerçek lokusunun bulunduğu yere yakın bir yerde).

Bir iddiaya örnek olarak, belirli bir yöntem grubunun doğru sırada çağrıldığını kontrol etmek (ör. hasNext() önce çağrılır next()bir in Iterator).


1
Next () öğesinden önce hasNext () öğesini çağırmanız gerekmez.
DJClayworth

6
@DJClayworth: İddiaları tetiklemekten de kaçınmanıza gerek yok. :-)
Donal Fellows 03

8

Java'daki assert anahtar kelimesi ne işe yarar?

Derlenmiş bayt koduna bakalım.

Sonuç olarak:

public class Assert {
    public static void main(String[] args) {
        assert System.currentTimeMillis() == 0L;
    }
}

neredeyse aynı bayt kodunu oluşturur:

public class Assert {
    static final boolean $assertionsDisabled =
        !Assert.class.desiredAssertionStatus();
    public static void main(String[] args) {
        if (!$assertionsDisabled) {
            if (System.currentTimeMillis() != 0L) {
                throw new AssertionError();
            }
        }
    }
}

nerede Assert.class.desiredAssertionStatus()olduğu truezaman-ea aksi Komut satırına geçirilen ve yanlış olduğunu.

System.currentTimeMillis()Optimize edilmemesini sağlamak için kullanıyoruz ( assert true;yaptı).

Sentetik alan, Java'nın Assert.class.desiredAssertionStatus()yükleme sırasında yalnızca bir kez çağrı yapması için oluşturulur ve ardından sonucu burada önbelleğe alır. Ayrıca bakınız: "Statik sentetik" in anlamı nedir?

Bunu aşağıdakilerle doğrulayabiliriz:

javac Assert.java
javap -c -constants -private -verbose Assert.class

Oracle JDK 1.8.0_45 ile sentetik bir statik alan üretildi (ayrıca bkz: "statik sentetik" in anlamı nedir? ):

static final boolean $assertionsDisabled;
  descriptor: Z
  flags: ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC

statik bir başlatıcı ile birlikte:

 0: ldc           #6                  // class Assert
 2: invokevirtual #7                  // Method java/lang Class.desiredAssertionStatus:()Z
 5: ifne          12
 8: iconst_1
 9: goto          13
12: iconst_0
13: putstatic     #2                  // Field $assertionsDisabled:Z
16: return

ve ana yöntem:

 0: getstatic     #2                  // Field $assertionsDisabled:Z
 3: ifne          22
 6: invokestatic  #3                  // Method java/lang/System.currentTimeMillis:()J
 9: lconst_0
10: lcmp
11: ifeq          22
14: new           #4                  // class java/lang/AssertionError
17: dup
18: invokespecial #5                  // Method java/lang/AssertionError."<init>":()V
21: athrow
22: return

Şu sonuca varıyoruz ki:

  • için bayt kodu düzeyi desteği yoktur assert: bir Java dili konseptidir
  • assertkomut satırında -Pcom.me.assert=truedeğiştirilecek sistem özellikleri ile oldukça iyi bir şekilde taklit edilebilir -eave a throw new AssertionError().

2
Yani catch (Throwable t)madde de iddia ihlallerini yakalayabiliyor mu? Benim için faydalarını sadece iddianın gövdesinin zaman alıcı olduğu durumla sınırlandırır, bu nadirdir.
Evgeni Sergeev

1
Neden iddianın yararlılığını sınırladığından emin değilim. Çok nadir durumlar dışında asla bir Throwable'ı yakalamamalısınız. Throwable'ı yakalamanız gerekiyorsa, ancak iddiaları yakalamamasını istiyorsanız, sadece AssertionErrorilkini yakalayıp yeniden yapabilirsiniz .
MiguelMunoz

7

Stack sınıfından gerçek bir dünya örneği ( Java Makalelerindeki Onaylama'dan )

public int pop() {
   // precondition
   assert !isEmpty() : "Stack is empty";
   return stack[--num];
}

80
Bu C: 'da kaşlarını çattı. Donal'ın cevabına bakınız.
Konerak

4
Katılıyorum. Bu resmi bir öğreticiden alınmış olsa da, kötü bir örnek.
DJClayworth


7
Muhtemelen orada bir bellek sızıntısı var. Stack [num] = null; GC'nin işini düzgün yapması için.
H.Rabiee

3
Özel bir yöntemde, bir sınıf veya yöntemin arızalanması için istisnalar olması tuhaf olacağından, bir iddia kullanmak doğru olur. Genel bir yöntemde, dışarıdan bir yerden çağırmak, diğer kodun nasıl kullandığını gerçekten söyleyemezsiniz. Gerçekten isEmpty () öğesini kontrol ediyor mu değil mi? Bilmiyorsun.
Vlasec

7

Bir iddia, koddaki hataları tespit etmeyi sağlar. Test ve hata ayıklama için, programınız hazırlanırken bunları kapalı bırakarak iddiaları açabilirsiniz.

Bunun doğru olduğunu bildiğinizde neden bir şey iddia ediyorsunuz? Bu sadece her şey düzgün çalışıyorsa geçerlidir. Programda bir kusur varsa, aslında doğru olmayabilir. Bunu işlemin başlarında tespit etmek, bir şeyin yanlış olduğunu bilmenizi sağlar.

Bir assertifade, isteğe bağlı bir Stringmesajla birlikte bu ifadeyi içerir .

Bir assert ifadesinin sözdiziminin iki formu vardır:

assert boolean_expression;
assert boolean_expression: error_message;

Aşağıda, iddiaların nerede ve nerede kullanılmaması gerektiğini belirleyen bazı temel kurallar verilmiştir. İddialar gerektiğini kullanılacak:

  1. Özel bir yöntemin giriş parametrelerini doğrulama. Herkese açık yöntemler için DEĞİLDİR . publicyöntemler kötü parametreler iletildiğinde düzenli istisnalar atmalıdır.

  2. Programın herhangi bir yerinde, neredeyse kesinlikle doğru olan bir gerçeğin geçerliliğini sağlamak.

Örneğin, yalnızca 1 veya 2 olacağından eminseniz, aşağıdaki gibi bir iddia kullanabilirsiniz:

...
if (i == 1)    {
    ...
}
else if (i == 2)    {
    ...
} else {
    assert false : "cannot happen. i is " + i;
}
...
  1. Herhangi bir yöntemin sonunda post koşullarını doğrulama. Bu, iş mantığını yürüttükten sonra, değişkenlerinizin veya sonuçlarınızın dahili durumunun beklediğinizle tutarlı olmasını sağlamak için iddiaları kullanabileceğiniz anlamına gelir. Örneğin, bir soketi veya dosyayı açan bir yöntem, soketin veya dosyanın gerçekten açıldığından emin olmak için sonunda bir onaylama kullanabilir.

İddialar olmamalıdır için kullanılabilir:

  1. Genel bir yöntemin giriş parametrelerini doğrulama. İddialar her zaman uygulanamayabileceğinden, düzenli istisna mekanizması kullanılmalıdır.

  2. Kullanıcı tarafından girilen bir şey üzerindeki kısıtlamaları doğrulama. Yukarıdaki ile aynı.

  3. Yan etkiler için kullanılmamalıdır.

Örneğin, bu uygun bir kullanım değildir, çünkü burada iddia, doSomething()yöntemin çağrılmasının yan etkisi için kullanılır.

public boolean doSomething() {
...    
}
public void someMethod() {       
assert doSomething(); 
}

Bunun gerekçelendirilebileceği tek durum, kodunuzda iddiaların etkinleştirilip etkinleştirilmediğini bulmaya çalıştığınız durumdur:   

boolean enabled = false;    
assert enabled = true;    
if (enabled) {
    System.out.println("Assertions are enabled");
} else {
    System.out.println("Assertions are disabled");
}

5

Burada verilen tüm harika cevaplara ek olarak, resmi Java SE 7 programlama kılavuzunun kullanımı hakkında oldukça özlü bir el kitabı vardır assert; iddiaları kullanmanın iyi (ve önemli olarak kötü) bir fikir olduğu ve istisnalar atmanın ne kadar farklı olduğuna dair birkaç örnek.

bağlantı


1
Katılıyorum. Makalenin birçok mükemmel örneği var. Özellikle nesne bir kilit tutarken bir yöntem çağrıldığından emin olmak için birini sevdim.
MiguelMunoz

4

Assert gelişirken çok faydalıdır. Bir şey yapamadığında kullanırsın kodunuzu doğru çalışıp çalışmadığını olur. Kullanımı kolaydır ve kodda sonsuza kadar kalabilir, çünkü gerçek hayatta kapatılacaktır.

Durumun gerçek hayatta ortaya çıkma şansı varsa, o zaman ele almalısınız.

Bayıldım ama Eclipse / Android / ADT'de nasıl açılacağını bilmiyorum. Hata ayıklama sırasında bile kapalı görünüyor. (Bu konuda bir iş parçacığı vardır, ancak ADT Çalışma Yapılandırmasında görünmeyen 'Java vm' anlamına gelir).


1
Tutulma IDE'sinde iddiayı etkinleştirmek için lütfen tutoringcenter.cs.usfca.edu/resources/…
Ayaz Paşa

Android'de ekleri açmanın bir yolu olduğunu düşünmüyorum. Bu çok hayal kırıklığı yaratıyor.
MiguelMunoz

3

İşte bir Hibernate / SQL projesi için bir sunucuda yazdığım bir iddia. Bir varlık fasulyesinin isActive ve isDefault adı verilen iki etkin-boolean özelliği vardı. Her birinin değeri "Y" veya "N" veya null olabilir, bu değer "N" olarak kabul edilir. Tarayıcı istemcisinin bu üç değerle sınırlı olduğundan emin olmak istiyoruz. Yani, bu iki özellik için seterlerimde, bu iddiayı ekledim:

assert new HashSet<String>(Arrays.asList("Y", "N", null)).contains(value) : value;

Aşağıdakilere dikkat edin.

  1. Bu iddia sadece geliştirme aşaması içindir. Müşteri kötü bir değer gönderirse, üretime ulaşmadan çok önce bunu yakalayıp düzeltiriz. İddialar erken yakalayabileceğiniz kusurlar içindir.

  2. Bu iddia yavaş ve verimsizdir. Sorun yok. İddialar yavaş olmakta serbesttir. Umurumuzda değil çünkü sadece geliştirme araçları. Bu, üretim kodunu yavaşlatmayacaktır çünkü iddialar devre dışı bırakılacaktır. (Bu konuda daha sonra alacağım bazı anlaşmazlıklar var.) Bu benim bir sonraki noktama götürüyor.

  3. Bu iddianın yan etkisi yoktur. Değişimimi değiştirilemez bir statik final Setine karşı test edebilirdim, ancak bu set hiç kullanılmayacağı üretimde kaldı.

  4. Bu iddia, istemcinin düzgün çalıştığını doğrulamak için mevcuttur. Üretime ulaştığımızda, müşterinin düzgün çalıştığından emin olacağız, böylece iddiayı güvenli bir şekilde kapatabiliriz.

  5. Bazı insanlar bunu soruyor: Üretimde iddiaya ihtiyaç duyulmuyorsa, neden sadece işiniz bittiğinde bunları çıkarmıyorsunuz? Çünkü bir sonraki versiyonda çalışmaya başladığınızda hala onlara ihtiyacınız olacak.

Bazı insanlar asla iddiaları kullanmamanız gerektiğini savundu, çünkü tüm hataların gittiğinden asla emin olamazsınız, bu yüzden onları üretimde bile tutmanız gerekir. Ve böylece assert ifadesini kullanmanın bir anlamı yok, çünkü iddia etmenin tek avantajı onları kapatabiliyor olmanız. Bu nedenle, bu düşünceye göre, (neredeyse) asla takıntı kullanmamalısınız. Katılmıyorum. Bir test üretime aitse, bir iddia kullanmamanız kesinlikle doğrudur. Ama bu test yapar değil üretime ait . Bu, üretime hiç ulaşması muhtemel olmayan bir hatayı yakalamak içindir, bu nedenle işiniz bittiğinde güvenle kapatılabilir.

BTW, şöyle yazmış olabilirdim:

assert value == null || value.equals("Y") || value.equals("N") : value;

Bu sadece üç değer için iyidir, ancak olası değerlerin sayısı artarsa ​​HashSet sürümü daha uygun hale gelir. Verimlilik konusunu vurgulamak için HashSet sürümünü seçtim.


Böyle küçük bir a kullanmanın HashSet, herhangi bir hız avantajı sağladığından şüpheliyim ArrayList. Dahası, set ve liste kreasyonları arama süresine hakimdir. Sabit kullanırken iyi olurlar. Hepsi dedi +1.
maaartinus

Hepsi doğru. İddianamelerin yavaş olması konusunda özgür olduğumu göstermek için bu verimsiz yolu yaptım. Bu daha verimli hale getirilebilir, ancak yapamayan başkaları da vardır. "Katı Kod Yazma" adlı mükemmel bir kitapta Steve Maguire, Microsoft Excel'de, değişmemesi gereken hücreleri atlayan yeni artımlı güncelleme kodunu test etmesini söyler. Kullanıcı her değişiklik yaptığında, sonuçların artımlı güncelleme özelliğiyle sonuçların eşleştiğinden emin olmak için onaylama tüm e-tabloyu yeniden hesaplar. Hata ayıklama sürümünü gerçekten yavaşlattı, ancak tüm hatalarını erken yakaladılar.
MiguelMunoz

Tamamen kabul etti. İddialar bir çeşit testtir - sıradan testlerden daha az çok yönlüdür, ancak özel yöntemleri kapsayabilirler ve yazmak çok daha ucuzdur. Onları daha fazla kullanmaya çalışacağım.
maaartinus

2

İddia temelde uygulamada hata ayıklamak için kullanılır veya bir uygulamanın geçerliliğini kontrol etmek için bazı uygulamalar için kural dışı durum işleme yerine kullanılır.

İddia çalışma zamanında çalışır. Tüm kavramı çok basit bir şekilde açıklayabilen basit bir örnek, buradadır - assert anahtar kelimesi Java'da ne yapar? (WikiAnswers).


2

İddialar varsayılan olarak devre dışıdır. Bunları etkinleştirmek için programı -easeçeneklerle çalıştırmalıyız (ayrıntı düzeyi değişebilir). Örneğin java -ea AssertionsDemo,.

İddiaları kullanmak için iki format vardır:

  1. Basit: örn. assert 1==2; // This will raise an AssertionError.
  2. Daha İyi: assert 1==2: "no way.. 1 is not equal to 2"; Bu, verilen mesajın da görüntülenmesiyle bir AssertionError hatası oluşturur ve bu nedenle daha iyidir. Gerçek sözdizimi assert expr1:expr2expr2'nin bir değer döndüren herhangi bir ifade olabilmesine rağmen, bunu yalnızca bir ileti yazdırmak için daha sık kullandım.

1

Özetlemek gerekirse (ve bu sadece Java için değil, birçok dil için de geçerlidir):

"assert", hata ayıklama işlemi sırasında öncelikle yazılım geliştiricileri tarafından bir hata ayıklama yardımcısı olarak kullanılır. Onay mesajları asla görünmemelidir. Birçok dil, "üretim" kodunun üretilmesinde kullanılmak üzere tüm "varsayımların" yok sayılmasına neden olacak bir derleme zamanı seçeneği sunar.

"istisnalar", mantık hatalarını temsil etseler de etmeseler de her türlü hata koşulunu ele almanın kullanışlı bir yoludur, çünkü devam edemeyeceğiniz bir hata koşuluyla karşılaşırsanız, bunları "havaya fırlatabilir, "Nerede olursanız olun, başka birisinin onları" yakalamaya "hazır olmasını bekliyorsunuz. Kontrol, istisnayı atan koddan doğrudan yakalayıcının eldivenine tek adımda aktarılır. (Ve alıcı gerçekleşen çağrıların tam geri izini görebilir.)

Dahası, bu altyordamın arayanları, altyordamın başarılı olup olmadığını kontrol etmek zorunda değildir: "şimdi buradaysak, başarılı olmalıydı, çünkü aksi halde bir istisna atardı ve şimdi burada olmazdık!" Bu basit strateji kod tasarımını ve hata ayıklamayı çok daha kolay hale getirir.

İstisnalar, ölümcül hata koşullarının oldukları gibi olmasını sağlar: "kuralın istisnaları". Ve onlar için de "kuralın bir istisnası ... " sinek topu!


1

İddialar kapatılabilecek kontrollerdir. Nadiren kullanılırlar. Neden?

  • Genel yöntem bağımsız değişkenlerini denetlemek için kullanılmamalıdır, çünkü bunlar üzerinde denetiminiz yoktur.
  • Bu result != nulltür kontroller çok hızlı olduğu ve kaydedilecek pek bir şey olmadığı gibi basit kontroller için kullanılmamalıdır .

Peki geriye ne kaldı? Gerçekten doğru olması beklenen koşullar için pahalı kontroller . Buna iyi bir örnek, RB ağacı gibi bir veri yapısının değişmezleri olacaktır. Aslında, ConcurrentHashMapJDK8'de, bu tür birkaç anlamlı iddia vardır TreeNodes.

  • Onları üretimde açmak istemezsiniz, çünkü çalışma süresine kolayca hakim olabilirler.
  • Testler sırasında bunları açmak veya kapatmak isteyebilirsiniz.
  • Kod üzerinde çalışırken kesinlikle onları açmak istiyorsunuz.

Bazen, çek gerçekten pahalı değil, ama aynı zamanda oldukça eminsin, geçecek. Kodumda, örneğin,

assert Sets.newHashSet(userIds).size() == userIds.size();

Yeni oluşturduğum listenin benzersiz öğeleri olduğundan eminim, ancak bunu belgelemek ve tekrar kontrol etmek istedim.


0

Temel olarak, "assert true" geçer ve "assert false" başarısız olur. Bunun nasıl çalışacağına bakalım:

public static void main(String[] args)
{
    String s1 = "Hello";
    assert checkInteger(s1);
}

private static boolean checkInteger(String s)
{
    try {
        Integer.parseInt(s);
        return true;
    }
    catch(Exception e)
    {
        return false;
    }
}

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.