Java 8 alıcıları isteğe bağlı türü döndürmeli mi?


288

Optional Java 8'de tanıtılan tür birçok geliştirici için yeni bir şeydir.

Optional<Foo>Klasik yerine türü döndüren bir alıcı yöntemi Fooiyi bir uygulama mı? Değerin olabileceğini varsayın null.


8
Her ne kadar bu durum tartışmalı cevaplar çekse de, iyi bir soru. Konuyla ilgili gerçek bilgiler içeren bir cevap bekliyorum.
Justin

8
Soru nullablititenin kaçınılmaz olup olmadığıdır. Bir bileşen, null değerine izin verilen bir özelliğe sahip olabilir, ancak yine de, bu bileşeni kullanan programcı bu özelliği kesinlikle açık tutmaya karar verebilir null. Yani programcı Optionalo zaman uğraşmak zorunda kalmamalı . Veya başka bir deyişle, nullbir aramanın sonucundaki ( Optionaluygun olan yerlerde ) olduğu gibi bir değerin yokluğunu temsil eder veya nullolası değerler kümesinin yalnızca bir üyesidir.
Holger

1
Ayrıca @NotNullek açıklamalar hakkındaki tartışmaya bakın : stackoverflow.com/q/4963300/873282
koppor

Yanıtlar:


517

Tabii ki insanlar istediklerini yapacaklar. Ama biz bu özelliği eklerken net bir niyeti var mı ve o was değil birçok kişi bunu bize isterdi gibi genel amaçlı Belki kadar, yazın olmak. Niyetimiz, "sonuç yok" u temsil etmenin açık bir yolunun gerekli olduğu kütüphane yöntemi geri dönüş türleri için sınırlı bir mekanizma sağlamaktı ve bunun için kullanmanın nullezici bir şekilde hatalara neden olması muhtemeldi.

Örneğin, muhtemelen hiçbir zaman bir sonuç dizisi veya bir sonuç listesi döndüren bir şey için kullanmamalısınız; bunun yerine boş bir dizi veya liste döndürün. Neredeyse hiçbir zaman bir şeyin alanı veya bir yöntem parametresi olarak kullanmamalısınız.

Alıcılar için bir dönüş değeri olarak rutin olarak kullanmak kesinlikle aşırı kullanım olacağını düşünüyorum.

İsteğe bağlı olarak, kaçınılması gereken yanlış bir şey yoktur , sadece birçok insanın olmasını istediği şey değildir ve bu nedenle gayretli aşırı kullanım riski konusunda oldukça endişeliydik.

(Kamu hizmeti duyuru: ASLA diyoruz Optional.getsen boş değerli asla ispat edemedikleri sürece, bunun yerine gibi güvenli yöntemlerden birini kullanın orElseveya ifPresent. Geriye doğru bakıldığında, biz çağrıda bulundular getgibi bir şey getOrElseThrowNoSuchElementExceptionya bu son derece tehlikeli bir yöntem olduğunu o kadar açık hale şey Bu Optional, ilk etapta tüm amacını baltaladı . Öğrenilen ders. (GÜNCELLEME: Java 10 Optional.orElseThrow(), anlamsal olarak eşdeğer get(), ancak adı daha uygun olan).


24
(Son bölümle ilgili olarak)… ve emin olduğumuzda, değerin asla nullkullanamayacağımızdan emin olabiliriz orElseThrow(AssertionError::new), ahem veya orElseThrow(NullPointerException::new)
Holger

25
Niyetinizi eğer farklı ne yapardınız etmişti Belki veya bazı tip genel amaçlı tanıtmak olmuştur? İsteğe bağlı bir fatura için uygun olmayan bir yolu var mı, ya da sadece yeni bir API üzerinde Optionals tanıtımı onu Java-ish yapmaz mı?
David Moles

22
"Genel amaçlı tür" derken, bunu, ona yaklaşan bir kütüphane sınıfı sağlamak yerine, dilin tür sistemine yerleştirmek istedim. (Bazı dillerde T? (T veya null) ve T! (Nullable T) türleri vardır) İsteğe bağlı olarak yalnızca bir sınıf vardır; dil desteğiyle yapabileceğimiz gibi Foo ve İsteğe Bağlı <Foo> arasında örtük dönüşümler yapamayız.
Brian Goetz

11
Java dünyasındaki vurgunun neden İsteğe bağlı olduğunu ve daha iyi statik analiz olmadığını merak ettim. İsteğe bağlı bazı avantajları vardır, ancak büyük avantajı nullgeriye dönük uyumluluktur; Map::getbir boş değer döndürür V, bir değil döndürür Optional<V>ve bu asla değişmez. Yine de kolayca açıklanabilirdi @Nullable. Şimdi , değer eksikliğini ifade etmek için iki
yolumuz var

21
Ben bir özellik için bir dönüş değeri olarak kullanmanın hepiniz burada StackOverflow bu yanıtı okuyana kadar amaçlanan bir fikrim yoktu. Aslında, Oracle'ın web sitesinde bu makaleyi okuduktan sonra tam olarak ne düşündüğüne inanmaya yöneldim : oracle.com/technetwork/articles/java/… Açıkladığınız için teşekkür ederiz
Jason Thompson

73

Kendi araştırmamı yaptıktan sonra, bunun uygun olduğu zaman önerilebilecek bir takım şeylerle karşılaştım. En yetkili, bir Oracle makalesinden aşağıdaki alıntıdır:

"Optional sınıfının amacının her null referansın yerini almak olmadığını belirtmek önemlidir . Bunun yerine, amacı daha anlaşılır API'lerin tasarlanmasına yardımcı olmaktır, böylece sadece bir yöntemin imzasını okuyarak, Bu, sizi bir değerin yokluğu ile başa çıkmak için bir İsteğe Bağlı'yı aktif olarak açmaya zorlar. " - Boş İşaretçi İstisnalarından Bıktınız mı? Java SE 8 Kullanarak İsteğe Bağlı!

Bu alıntıyı Java 8'den de buldum İsteğe bağlı: Nasıl kullanılır?

"İsteğe bağlı, bu bağlamlarda kullanılmak üzere tasarlanmamıştır, çünkü bize hiçbir şey satın almaz:

  • alan modeli katmanında (serileştirilemez)
  • DTO'larda (aynı sebep)
  • yöntemlerin girdi parametrelerinde
  • yapıcı parametrelerinde "

Bu da bazı geçerli noktaları ortaya koyuyor gibi görünüyor.

OptionalKaçınılması gereken herhangi bir olumsuz çağrışım veya kırmızı bayrak bulamadım . Genel fikir, API'nizin kullanılabilirliğini iyileştirir veya geliştirirse, onu kullanmak olduğunu düşünüyorum.


11
stackoverflow.com/questions/25693309 - Jackson zaten destekliyor gibi görünüyor, bu yüzden "serileştirilemez" artık geçerli bir neden olarak geçmiyor :)
Vlasec

1
Yanıt yönteminizin neden Optionalyöntemlerin giriş parametrelerinde (daha özel olarak kurucular) kullanmanın "bize hiçbir şey satın almayacağını" önermenizi öneririm . dolszewski.com/java/java-8-optional-use-cases hoş bir açıklama içeriyor.
Gili

Diğer API'lara dönüştürülmesi gereken isteğe bağlı sonuçların kıvrılmasının İsteğe bağlı bir parametre gerektirdiğini gördüm. Sonuç oldukça anlaşılabilir bir API. Bkz. Stackoverflow.com/a/31923105/105870
Karl Pagan

1
Bağlantı koptu. Lütfen cevabı güncelleyebilir misiniz?
softarn

2
Sorun, geliştiricinin en iyi niyetine rağmen, genellikle API'yı geliştirmemesi. Tümü kontrol edebileceğiniz üretim kodundan alınan İsteğe bağlı kötü kullanımların koleksiyon örneklerini aldım .
MiguelMunoz

20

Genel olarak null olabilecek dönüş değerleri için isteğe bağlı türü kullanmak iyi bir fikir olduğunu söyleyebilirim. Bununla birlikte, çerçevelerle wrt Klasik alıcıları isteğe bağlı türlerle değiştirmenin, alıcılar ve ayarlayıcılar için kodlama kurallarına dayanan çerçevelerle (örneğin, Hazırda Bekletme) çalışırken çok fazla soruna neden olacağını varsayıyorum.


14
Bu tavsiye, stackoverflow.com/a/26328555/3553087 adresindeki "gayretli aşırı kullanım riskinden endişe ediyoruz" ile kastettiğim türden bir şey .
Brian Goetz

13

OptionalJava'ya eklenmesinin nedeni şudur:

return Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
    .stream()
    .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
    .filter(m ->  Arrays.equals(m.getParameterTypes(), parameterClasses))
    .filter(m -> Objects.equals(m.getReturnType(), returnType))
    .findFirst()
    .getOrThrow(() -> new InternalError(...));

bundan daha temiz:

Method matching =
    Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
    .stream()
    .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
    .filter(m ->  Arrays.equals(m.getParameterTypes(), parameterClasses))
    .filter(m -> Objects.equals(m.getReturnType(), returnType))
    .getFirst();
if (matching == null)
  throw new InternalError("Enclosing method not found");
return matching;

Demek istediğim, isteğe bağlı olarak Java'ya aynı anda eklenen işlevsel programlamayı desteklemek için yazılmıştır . (Örnek , Brian Goetz tarafından bir blogun izniyle gelir . orElse()Bu kod zaten bir istisna atacağı için daha iyi bir örnek yöntemi kullanabilir , ancak resmi elde edersiniz.)

Ama şimdi, insanlar isteğe bağlı olarak çok farklı bir nedenle kullanıyorlar. Bunu dil tasarımındaki bir kusuru gidermek için kullanıyorlar. Kusur şudur: Bir API'nin parametrelerinin ve dönüş değerlerinden hangisinin boş olmasına izin verileceğini belirtmenin bir yolu yoktur. Javadoclarda bahsedilebilir, ancak çoğu geliştirici kodları için javadoc bile yazmaz ve pek çoğu javadocları yazarken kontrol etmez. Bu nedenle, çağrı yığınının dokuz veya on katı kadar defalarca onaylanmış olmalarına rağmen, genellikle boş olamazlarsa da, bunları kullanmadan önce her zaman null değerleri kontrol eden birçok koda yol açar.

Bence bu kusuru çözmek için gerçek bir susuzluk var, çünkü yeni İsteğe bağlı sınıfı gören birçok kişi amacının API'lara netlik katmak olduğunu varsayıyordu. Bu yüzden insanlar "alıcılar Opsiyonelleri iade etmeli mi?" Hayır, alıcının işlevsel programlamada kullanılmasını beklemediğiniz sürece, muhtemelen mümkün olmamalıdır. Aslında, İsteğe Bağlı Java API'sinin nerede kullanıldığına bakarsanız, temel olarak fonksiyonel programlamanın çekirdeği olan Stream sınıflarında bulunur. (Çok ayrıntılı bir şekilde kontrol etmedim, ancak Stream sınıfları kullandıkları tek yer olabilir .)

Bir alıcı kodunu fonksiyonel kodda kullanmayı planlıyorsanız, standart bir alıcıya ve İsteğe bağlı olarak dönen ikinci bir alıcıya sahip olmak iyi bir fikir olabilir.

Oh, ve sınıfınızın serileştirilebilir olması gerekiyorsa, kesinlikle İsteğe bağlı kullanmamalısınız.

Opsiyoneller API kusuruna karşı çok kötü bir çözümdür çünkü a) çok ayrıntılıdırlar ve b) Bu problemi asla en başta çözmeyi amaçlamamışlardır.

API kusuruna çok daha iyi bir çözüm Nullness Checker'dır . Bu, @Nullable ile açıklama ekleyerek hangi parametrelerin ve dönüş değerlerinin boş olmasına izin verileceğini belirlemenizi sağlayan bir ek açıklama işlemcisidir. Bu şekilde, derleyici kodu tarayabilir ve aslında null olabilecek bir değerin null değerine izin verilmeyen bir değere geçirilip geçirilmediğini anlayabilir. Varsayılan olarak, açıklama eklenmedikçe hiçbir şeyin boş kalmasına izin verilmediğini varsayar. Bu şekilde, null değerler konusunda endişelenmenize gerek kalmaz. Bir parametreye null değer iletilmesi derleyici hatasına neden olur. Bir nesneyi null olamaz olarak null için sınamak derleyici uyarısı oluşturur. Bunun etkisi, NullPointerException'ı bir çalışma zamanı hatasından derleme zamanı hatasına dönüştürmektir.

Bu her şeyi değiştirir.

Alıcılarınıza gelince, İsteğe Bağlı'yı kullanmayın. Ve sınıflarınızı tasarlamaya çalışın, böylece üyelerin hiçbiri boş olamaz. Ve belki de Nullness Checker'ı projenize eklemeyi ve ihtiyaç duymaları durumunda geters ve setter parametrelerinizi @Nullable olarak bildirmeyi deneyin. Bunu sadece yeni projelerle yaptım. Muhtemelen null için çok sayıda gereksiz testle yazılmış mevcut projelerde çok fazla uyarı üretiyor, bu nedenle güçlendirilmesi zor olabilir. Ama aynı zamanda birçok hata yakalayacak. Onu seviyorum. Kodum bu yüzden çok daha temiz ve daha güvenilir.

(Buna hitap eden yeni bir dil de var. Java bayt kodunu derleyen Kotlin, bir nesnenin bildirdiğinizde boş olup olmayacağını belirlemenizi sağlar. Daha temiz bir yaklaşımdır.)

Orijinal Gönderi Eki (sürüm 2)

Bir sürü düşünce verdikten sonra, isteksizce geri dönmenin kabul edilebilir olduğu sonucuna vartım Bir koşulda isteğe bağlı: Alınan değerin aslında boş olabileceği. Ben insanlar rutin olarak döndürmek kodu bir sürü gördüm İsteğe bağlı muhtemelen null döndüremez alıcılar. Bu sadece hata daha olası kılan, kod karmaşıklığı ekleyen çok kötü bir kodlama uygulaması olarak görüyorum. Ancak, döndürülen değer gerçekten boş olabiliyorsa, devam edin ve bir İsteğe Bağlı içine sarın.

İşlevsel programlama için tasarlanan ve işlev başvurusu gerektiren yöntemlerin, biri İsteğe Bağlı kullanan iki biçimde yazılacağını (ve yapılması gerektiğini) unutmayın. Örneğin Optional.map()ve Optional.flatMap()her ikisi de işlev referanslarını alır. Birincisi sıradan bir alıcıya bir referans alır ve ikincisi İsteğe bağlı olanı alır. Yani değerin boş olamayacağı bir İsteğe bağlı döndürerek kimseye bir iyilik yapmazsınız.

Tüm bunları söyledikten sonra, NullPointerExceptions zaman hatalarını derlemek için çalışma zamanı hatalarından çevirdikleri için Nullness Checker tarafından kullanılan yaklaşımın null'larla başa çıkmanın en iyi yolu olduğunu görüyorum .


2
Bu cevap benim için en uygun görünüyor. İsteğe bağlı olarak yalnızca java 8'de akış eklendiğinde eklenmiştir. Ve gördüğüm kadarıyla yalnızca akış işlevleri isteğe bağlı olarak geri dönüyor.
Archit

1
Bence bu en iyi cevaplardan biri. İnsanlar Neyin Opsiyonel olduğunu ve nasıl çalıştığını bilir. Ancak en kafa karıştırıcı kısmı, nerede kullanılacağı ve nasıl kullanılacağıydı. bunu okuyarak birçok şüpheyi temizler.
ParagFlume

3

Eğer modern serializers ve anlaşılması diğer çerçeveler kullanıyor Optionalo zaman ben bu bulduk kurallar yazarken iyi iş Entityfasulye ve etki alanı katmanları:

  1. Serileştirme katmanı (genellikle bir DB) tablodaki nullsütundaki bir hücre için bir değere izin verirse, alıcı geliştiriciye bu değerin makul olması beklendiğini ve bunu işlemesi gerektiğini göstererek geri dönebilir . DB değeri boş olmayacak garanti o zaman alıcı olmalıdır değil bir in sar .BARFOOFoo.getBar()OptionalOptional
  2. Foo.barolmalı privateve değil olması Optional. Olması için hiçbir sebep yoktur OptionalEğer öyleyse private.
  3. Ayarlayıcı Foo.setBar(String bar)türünü almalı barve değil Optional . Bir nullbağımsız değişken kullanmakta sorun yoksa, bunu JavaDoc yorumunda belirtin. Kullanmak için Tamam değilse nullbir IllegalArgumentExceptionveya birkaç uygun iş mantığı IMHO, daha uygun olduğunu.
  4. Yapıcıların Optionalargümanlara ihtiyacı yoktur (3. maddeye benzer nedenlerle). Genellikle sadece yapıcı bağımsız değişkenleri şunlardır gerekir seri veritabanında olmayan boş.

Yukarıda daha verimli hale getirmek için, alıcıları üreten ve şablonları tekabül için IDE şablonlarını düzenlemek isteyebilirsiniz toString(), equals(Obj o)(çoğu IDE jeneratörleri zaten boş değerlere başa) olanlar için doğrudan vb veya kullanım alanları.

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.