Neden Optional.get () isPresent () 'ı çağırmadan kötü, ama iterator.next ()' dir?


23

Bir koleksiyondaki belirli bir öğeyi bulmak için yeni Java8 akışlarını api kullanırken, şöyle kod yazarım:

    String theFirstString = myCollection.stream()
            .findFirst()
            .get();

Burada IntelliJ, önce isPresent () öğesini kontrol etmeden get () işlevinin çağrıldığını uyarır.

Ancak bu kod:

    String theFirstString = myCollection.iterator().next();

... uyarı vermez.

İki teknik arasında, akış yaklaşımını bir şekilde daha "tehlikeli" yapan, ilk önce isPresent () işlevini çağırmadan get () işlevinin asla çağrılmaması çok önemlidir. Çevrenizdeyken, programcıların İsteğe bağlı <> ile nasıl dikkatsiz olduklarından bahseden makaleler bulurum ve ne zaman istediklerini get () olarak adlandırabilirler.

Sorun şu ki, kodumun arabası olduğu yere mümkün olduğunca yakın bir yere atılmayı seviyorum, bu durumda, bulunacak bir öğe olmadığında get () olarak çağırdığım yerde olur. Gibi işe yaramaz denemeler yazmak istemiyorum:

    Optional<String> firstStream = myCollection.stream()
            .findFirst();
    if (!firstStream.isPresent()) {
        throw new IllegalStateException("There is no first string even though I totally expected there to be! <sarcasm>This exception is much more useful than NoSuchElementException</sarcasm>");
    }
    String theFirstString = firstStream.get();

... habersiz olduğum () dan istisnaları kışkırtma tehlikesi olmadığı sürece?


Karl Bielefeldt'in yanıtını ve Hulk'un yorumunu okuduktan sonra yukarıdaki istisna kodumun biraz sakar olduğunu farkettim, işte daha iyi bir şey:

    String errorString = "There is no first string even though I totally expected there to be! <sarcasm>This exception is much more useful than NoSuchElementException</sarcasm>";
    String firstString = myCollection.stream()
            .findFirst()
            .orElseThrow(() -> new IllegalStateException(errorString));

Bu daha kullanışlı görünüyor ve birçok yerde muhtemelen doğal olacak. Hala bunlarla uğraşmak zorunda kalmadan bir listeden bir öğe seçmek isteyeceğimi hissediyorum, ancak bu tür yapılara alışmam gerekebilir.


7
İstisna vurma kısmı, son örneğinizi oluşturur ya daElseThrow - kullanırsanız çok daha kısa yazılabilir ve istisna atmayı düşündüğünüz tüm okuyucular için açık olacaktır.
Hulk

Yanıtlar:


12

Her şeyden önce, kontrolün karmaşık olduğunu ve muhtemelen nispeten zaman alıcı olduğunu düşünün. Bazı statik analizler gerektirir ve iki arama arasında bir miktar kod olabilir.

İkincisi, iki yapının deyimsel kullanımı çok farklı. OptionsJava’dan çok daha sık kullanan Scala’da tam zamanlı program yapıyorum . .get()Üzerinde kullanmam gereken tek bir örneği hatırlayamıyorum Option. Neredeyse her zaman Optionalişlevinizden geri dönüyor olmalısınız , yoksa orElse()yerine kullanıyor olmalısınız . Bunlar, eksik değerleri işlemek için istisnalar atmaktan çok daha üstün yollardır.

Çünkü .get()nadiren normal kullanımda çağrılmalıdır bir gördüğünde bir IDE karmaşık denetimini başlatmak için, bu mantıklı .get()düzgün kullanıldı olmadığını görmek için. Buna karşılık, iterator.next()bir yineleyicinin uygun ve her yerde kullanılmasıdır.

Başka bir deyişle, birinin kötü olması, diğeri olmaması meselesi değil, birinin çalışması için temel olan ve geliştirici testi sırasında istisna atama olasılığı yüksek olan ve diğerinin nadiren kullanılan bir durum olması sorunudur. Geliştirici testi sırasında bir istisna atmaya yetecek kadar hareket etmemiş olabilir. Bunlar IDE'lerin sizi uyarmasını istediğiniz türden davalar.


İsteğe bağlı bu iş hakkında daha fazla şey öğrenmeye ihtiyacım olabilir - java8 işlerini çoğunlukla webapp'ımızda çokça yaptığımız nesne listelerinin haritasını çıkarmanın iyi bir yolu olarak gördüm. Yani her yere İsteğe Bağlı <> s göndermeniz mi gerekiyor? Bu alışmak biraz zaman alacak, ama bu başka bir SE yazısı! :)
TV'nin Frank

@ TV'nin Frank'ı Her yerde olması gerekenden daha az ve içerdiği türün değerini dönüştüren işlevler yazmanıza izin verdiğinden daha az, onları mapveya ile zincirlersiniz flatMap. Optional's çoğunlukla her şeyi nullçek ile tutturmak zorunda kalmamak için harika bir yoldur .
Morgen

15

İsteğe bağlı sınıf, içerdiği öğenin mevcut olup olmadığı bilinmiyorsa kullanılmaya yöneliktir. Uyarı, programcılara incelikle ele almaları mümkün olabilecek ek bir kod yolu olduğunu bildirmek için mevcuttur. Fikir, istisnaların hiç atılmamasıdır. Sunacağınız kullanım durumu ne için tasarlandığı değildir ve bu nedenle uyarı sizin için önemli değildir - gerçekten bir istisna atılmak istiyorsanız, devam edin ve devre dışı bırakın (yalnızca bu satır için, küresel olarak değil - Mevcut olmayan olayı bir örnek olarak ele alınıp almamaya karar vermek ve uyarı size bunu yapmayı hatırlatır.

Muhtemelen, yineleyiciler için benzer bir uyarı üretilmelidir. Ancak, basitçe asla yapılmamıştır ve şimdi bir tane eklemek muhtemelen mevcut projelerdeki çok fazla uyarının iyi bir fikir olmasına neden olacaktır.

Ayrıca, kendi istisnalarınızı atmanızın, hangi öğenin olması beklendiğinin bir açıklamasını da dahil eden, ancak daha sonraki bir noktada hata ayıklama konusunda pek de yardımcı olamayacağından, varsayılan bir istisnadan daha kullanışlı olabileceğini unutmayın.


6

Bunlarda doğal bir fark bulunmadığına katılıyorum, ancak daha büyük bir kusuru işaret etmek istiyorum.

Her iki durumda da, çalışması garanti edilmeyen bir yöntem sunan bir türünüz vardır ve bundan önce başka bir yöntem çağırmanız gerekir. IDE / compiler / etc'inizin bir dava hakkında mı yoksa diğerinden sizi uyarması, sadece bu davayı destekleyip desteklemediğine ve / veya bunu yapacak şekilde yapılandırmanıza bağlı olarak değişir.

Yine de söyleniyor, her iki kod parçası da kod kokuyor. Bu ifadeler, temel tasarım hatalarını ima eder ve kırılgan kodlar verir.

Elbette hızlı bir şekilde başarısız olmak isteme konusunda haklısın, ama en azından benim için çözüm, sadece omuzlarını silkmek ve ellerini havaya (veya yığına bir istisna) atmak olamaz.

Koleksiyonunuzun şimdilik boş olmadığı biliniyor, ancak gerçekten güvenebileceğiniz tek şey türü: Collection<T>- ve bu boşluksuzluğu garanti etmiyor. Artık boş olmadığını bilseniz bile, değişen bir gereksinim, kodun önceden değiştirilmesine neden olabilir ve aniden kodunuzun tamamı boş bir koleksiyona çarptı.

Şimdi geri çekip başkalarına boş olmayan bir liste almanız gerektiğini söylemekte özgürsünüz. Bu 'hatayı' hızlı bulduğun için mutlu olabilirsin. Yine de, kırılmış bir yazılıma sahipsiniz ve kodunuzu değiştirmeniz gerekiyor.

Buna daha pratik bir yaklaşımla zıtlık yaratın: Koleksiyon topladığınızdan ve muhtemelen boş olabileceğinden, Opsiyonel # map, #orElse veya #get hariç bu yöntemlerden herhangi birini kullanarak bu durumla başa çıkmaya zorlarsınız.

Derleyici, sizin için mümkün olduğunca çok çalışarak, bir statik statü sözleşmesinde mümkün olduğu kadar güvenebilirsiniz Collection<T>. Kodunuz asla bu sözleşmeyi ihlal etmediğinde (bu iki kokulu çağrı zincirinden herhangi biri gibi), kodunuz değişen çevre koşullarına karşı daha az kırılgandır.

Yukarıdaki senaryoda, boş bir giriş toplama veren bir değişiklik kodunuz için hiçbir şekilde sonuç vermeyecektir. Bu sadece başka bir geçerli girdi ve bu yüzden hala doğru bir şekilde işlendi.

Bunun maliyeti, a) kırılgan kodları riske atmak yerine, daha iyi tasarımlara zaman ayırmak zorunda olmanızdır. B) istisnalar atarak gereksiz yere karmaşık şeyler.

Son bir notta: Tüm IDE'ler / derleyiciler iterator ().) () () Ya da isteğe bağlı.get () ön kontrolleri olmadan yapılan çağrılar hakkında uyarmadıkları için, bu tür kodlar genellikle incelemelerde işaretlenir. Ne pahasına olursa olsun, böyle bir kodun incelemeyi geçmesine izin vermek yerine temiz bir çözüm talep etmesine izin vermem.


Kod kokusu konusunda biraz katılıyorum sanırım - Kısa bir yazı yapmak için yukarıdaki örneği yaptım. Daha geçerli olan bir başka durum, bir listeden zorunlu bir eleman almaktır; bu bir uygulama IMO'sunda görünmesi garip bir şey değildir.
TV Frank

Noktaya bak. "Listeden p özelliği olan elemanı seçin" -> list.stream (). Filter (p) .findFirst () .. ve yine de "eğer orada değilse?" Sorusunu sormaya mecburuz. .. günlük ekmek ve tereyağı. Eğer mandatoray ve "sadece orada" ise - neden onu bir sürü başka şeylere sakladınız?
Frank

4
Çünkü belki de veritabanından yüklenen bir liste. Belki de liste, başka bir işlevde toplanan istatistiksel bir koleksiyondur: A, B ve C nesnelerinin olduğunu biliyoruz, B için ne kadar olduğunu bulmak istediğimi biliyoruz.
TV Frank

Veritabanını bilmiyorum ama benimki sorgu başına en az bir sonuç garanti etmiyor. İstatistiksel koleksiyonlar için de aynı - kim her zaman boş olmayacaklarını söylüyor? Bunun ya da o unsurun olması gerektiğine dair bir nedenin basit olduğunu kabul ediyorum, ancak belgelere uygun statik yazmayı tercih ediyorum ya da bir tartışmadan biraz daha kötüye gidiyorum.
Frank,
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.