Neden değişkeni null kontrol etmek için isteğe bağlı bir tercihli kullanıyorsunuz?


25

İki kod örneğini alın:

if(optional.isPresent()) {
    //do your thing
}

if(variable != null) {
    //do your thing
}

En belirgin farkı söyleyebileceğim kadarıyla, İsteğe Bağlı'nın ek bir nesne oluşturmayı gerektirdiğidir.

Ancak, birçok kişi hızla Seçeneklileri kabul etmeye başladı. Opsiyonları null kontrolüne karşı kullanmanın avantajı nedir?


4
neredeyse hiç isPresent kullanmak istemiyorsanız, genellikle ifPresent veya map
jk

6
@jk. Çünkü ififadeler sooooo son on yıl ve herkes şimdi monad soyutlamalar ve lambdas kullanıyor.
user253751

2
@ immibis Bir zamanlar bu konuda başka bir kullanıcıyla uzun bir tartışma yaşadım. Mesele şu ki if(x.isPresent) fails_on_null(x.get), tip sisteminden çıkmanız ve kodun koşulla işlev çağrısı arasındaki (kuşkusuz kısa) mesafe boyunca "kafanızda" kırılmayacağının garantisini vermek zorundasınız. Gelen optional.ifPresent(fails_on_null)tipi sistemin sizin için bu garanti vermez ve endişe gerekmez.
ziggystar

1
Java'nın Optional.ifPresent(ve diğer çeşitli Java yapılarının) kullanımındaki ana kusur , yalnızca son değişkenleri etkili bir şekilde değiştirebilmeniz ve kontrol edilen istisnalar alamamanızdır. ifPresentMaalesef bu sık sık kaçınmak için yeterli bir sebep .
Mike,

Yanıtlar:


19

OptionalBaşında tüm yapmanız gereken işleri yapmak için tip sistemini kullanır: verilen bir referans olup olmadığını hatırlamak null. Bu iyi. Derleyicinin sıkıcı ilaç işleriyle uğraşmasına izin vermek ve yaratıcı, ilginç işler için insan düşüncesini ayırmak her zaman akıllıdır.

Olmadan Optional, kodunuzdaki her referans patlamamış bir bomba gibidir. Erişmek yararlı bir şey yapabilir veya başka bir istisna dışında programınızı sonlandırabilir.

İsteğe Bağlı ve olmadan null, normal bir referansa her erişim başarılı olur ve bir İsteğe Bağlı'ya yapılan tüm başvurular ayarlanmadıkça ve bunu kontrol edemezseniz başarılı olur. Bu sürdürülebilirlikte büyük bir kazanç.

Ne yazık ki, şu anda sunulan çoğu dil Optional kaldırılmamıştır null, bu nedenle konseptten yalnızca “kesinlikle hayır null, hiç” sıkı bir politika belirleyerek kâr elde edebilirsiniz . Bu nedenle, Optionalörneğin Java ideal olması gerektiği kadar zorlayıcı değildir.


Cevabınız en iyisi olduğundan, gelecekteki bunu okuyanlar için Lambdas kullanımını bir avantaj olarak kullanmaya değer olabilir (cevabımı görün)
William Dunne

Modern dillerin çoğu, geri dönüşü olmayan türler sağlar, Kotlin, Typescript, AFAIK.
Zen

5
IMO, bu daha iyi bir @Null notuyla ek açıklama ile ele alınır. OptionalSize sadece değerin boş olabileceğini hatırlatmak için hiçbir sebep yok
Hilikus

18

Bir Optionaldiğer cevaplar da dahil olduğu gibi başarısız olabilecek işlemlere daha güçlü bir yazma işlemi getirir, ancak buOptionals tabloya getirilen en ilginç veya değerli şeyden çok uzak . Çok daha faydalı olanı, başarısızlık kontrolünü geciktirme veya önleme ve başarısız olabilecek birçok işlemi kolayca oluşturma yeteneğidir.

optionalDeğişkeni örnek kodunuzdan alıp almadığınızı düşünün , ardından her birinin potansiyel olarak başarısız olabileceği iki ek adım uygulamanız gerekiyordu. Yol boyunca herhangi bir adım başarısız olursa, bunun yerine varsayılan bir değer dönmek istersiniz. OptionalsDoğru kullanarak , böyle bir şeyle bitirdiniz:

return optional.flatMap(x -> x.anotherOptionalStep())
               .flatMap(x -> x.yetAnotherOptionalStep())
               .orElse(defaultValue);

İle nullben için üç kez kontrol etmek zorunda kalacaktı nullkoduna karmaşıklığı ve bakım baş ağrısı bir çok ekler önce ilerlemeden. Optionalsbu çekimiflatMapve bu orElseişlevlerin .

Not Bir isPresentkez aramadım, hangi kodu kullanırken kullanacağınızı bir kod kokusu olarak düşünmelisiniz Optionals. Yani ille de gerektiği anlamına gelmez asla kullanmayınisPresent daha iyi bir yolu olup olmadığını görmek için, ayrıcalıkları herhangi bir kod inceleyerek ağır gerektiğini sadece. Aksi takdirde haklısınız, yalnızca kullanmaya göre marjinal bir güvenlik avantajı elde ediyorsunuz null.

Ayrıca, kodumun diğer kısımlarını boş işaretçilerden orta sonuçlardan korumak için, bunların tümünü tek bir işlevin içine alma konusunda endişeli olmadığımı unutmayın. .orElse(defaultValue)Örneğin, başka bir fonksiyona sahip olmak benim için daha mantıklıysa , oraya yerleştirme konusunda daha az vasıflarım var ve gerektiğinde farklı fonksiyonlar arasındaki işlemleri oluşturmak çok daha kolay.


10

Null'un (haklı ya da yanlış) iade edilmeyeceğini varsaydığı, geçerli bir cevap olarak sıfır olasılığını vurgular. Boş değerin geçerli olduğu birkaç kez vurgulamak, kodunuzu çok sayıda anlamsız boş denetimle kirletmenize izin vermez.

Bir değişkeni null değerine ideal olarak ayarlamak, isteğe bağlı herhangi bir yerde, herhangi bir yerde bir derleme zamanı hatası olur; çalışma zamanı boş gösterici istisnalarını ortadan kaldırarak. Açıkça geriye doğru uyumluluk bu ne yazık ki engelliyor


4

Sana bir örnek vereyim:

class UserRepository {
     User findById(long id);
}

Bu yöntemin neyi amaçladığı oldukça açık. Ancak depo, kimliği verilen kullanıcı içermiyorsa ne olur? Bir istisna atabilir ya da belki null döner?

İkinci örnek:

class UserRepository {
     Optional<User> findById(long id);
}

Şimdi, verilen kimliği olan bir kullanıcı yoksa, burada ne olur? Doğru, Optional.absent () örneğini alırsınız ve Optional.isPresent () ile kontrol edebilirsiniz. İsteğe Bağlı seçeneğiyle yöntem imzası, sözleşmesi hakkında daha açıktır. Yöntemin nasıl davranması gerektiği hemen anlaşılmıyor.

Ayrıca müşteri kodunu okurken bir faydası vardır:

User user = userRepository.findById(userId).get();

.Get () 'ı hemen kodlama kokusu olarak görmek için gözümü eğittim. Bu, müşterinin çağrılan yöntemin sözleşmesini bilerek ihmal ettiği anlamına gelir. Bunu Görmek İsteğe bağlı olmadan görmek çok daha kolaydır, çünkü bu durumda denilen yöntemin sözleşmesinin ne olduğunu (bunun karşılığında null olmasına izin verip vermeyeceğini) hemen farketmezsiniz, bu nedenle ilk önce kontrol etmeniz gerekir.

İsteğe bağlı, geri dönüş türü olan yöntemlerin, hiçbir zaman boş döndürmez ve genellikle (daha az güçlü) kurallarla, İsteğe Bağlı geri dönüş türü olmayan yöntemlerin, asla boş döndürmez (ancak, @Nonnull, @NotNull veya benzeri ile açıklama eklemek daha iyidir) yöntemiyle kullanılır. .


3

Bu Optional<T>, yöntemleriniz için geçerli bir yanıt / dönüş değeri olarak "başarısızlık" veya "sonuçsuz" olmanıza izin verir (örneğin, bir veritabanı aramasını düşünün). Arızayı belirtmek / Optionalkullanmak nullolmadığını göstermek yerine kullanmak yerine bazı avantajları vardır:

  • Açıkça "başarısızlık" olduğunu iletişim kurar olan bir seçenek. Metodunuzun kullanıcısı nulliade edilip edilmeyeceğini tahmin etmek zorunda değildir .
  • Değer null, bence en azından, gerektiği değil semantik tamamen açık olmayabilir olarak herhangi bir şey belirtmek için kullanılabilir. Çöp toplayıcıya daha önce belirtilen nesnenin toplanabileceğini söylemek için kullanılmalıdır.
  • OptionalSınıf değerleri ile koşullu işe çok güzel yöntemler sağlar (örneğin ifPresent()) ya da şeffaf bir şekilde varsayılan değerleri define ( orElse()).

Ne yazık ki, nesnenin hala olabileceği nullgibi koddaki denetimlere olan ihtiyacı tamamen ortadan kaldırmıyor .Optionalnull


4
İsteğe bağlı bunun için gerçekten değil. Bir hatayı bildirmek için bir istisna kullanın. Bunu yapamıyorsanız, içeren bir türü kullanmak ya bir sonuç veya bir hata kodu .
James Youngman

Kesinlikle katılmıyorum. Optional.empty (), benim görüşüme göre başarısızlık veya yokluğun gösterilmesinin harika bir yoludur.
LegendLength

@ LegendLength, başarısızlık durumunda kesinlikle katılmıyorum. Eğer bir fonksiyon başarısız olursa, bana sadece bir boş değil, "Ben başarısız oldum" demem daha iyi olur
Winston Ewert

Üzgünüm, katılıyorum, arama sırasında 'x' adında bir çalışan bulamaması gibi 'beklenen başarısızlık' demek istiyorum.
LegendLength

3

Diğer cevaplara ek olarak, seçeneklerin temiz kod yazmak söz konusu olduğunda diğer büyük avantajı, aşağıda gösterildiği gibi, değerin mevcut olması durumunda bir Lambda ifadesi kullanabilmenizdir:

opTr.ifPresent(tr -> transactions.put(tr.getTxid(), tr));

2

Aşağıdaki yöntemi göz önünde bulundurun:

void dance(Person partner)

Şimdi bazı arama koduna bakalım:

dance(null);

Bu, başka bir yerde kazayı izlemek için potansiyel olarak zor bir duruma neden olur, çünkü danceboş bir değer beklemiyordu.

dance(optionalPerson)

Bu derleme hatasına neden oluyor, çünkü dans bir Persondeğil bekliyordu.Optional<Person>

dance(optionalPerson.get())

Eğer bir kimsem olmasaydı, bu gayri meşru bir şekilde bir Kişiyi dönüştürmeye çalıştığım noktada bu istisnaya yol açar Optional<Person>. Anahtar, boş bırakmadan farklı olarak, buradaki izler dışında programdaki başka bir yer değil.

Hepsini bir araya getirmek için: isteğe bağlı yanlış kullanma, sorunları saptamanın daha kolay olmasını sağlar, boşu yanlış kullanmak sorunları takip etmenin zor olmasına neden olur.


1
Optional.get()Kodunuzu çağırırken istisnalar atıyorsanız , hatalı bir şekilde yazılır - Optional.isPresent()ilk önce (OP'de belirtildiği gibi) kontrol etmeden neredeyse hiçbir zaman Optional.get'i aramamalısınız veya yazabilirsiniz optionalPersion.ifPresent(myObj::dance).
Chris Cooper,

@QmunkE, evet, Optional.get () işlevini yalnızca isteğe bağlı seçeneğin boş olmadığını biliyorsanız (ya isPresent olarak adlandırdığınız ya da algoritmanızın garanti ettiği için) bilmelisiniz. Ancak, isPresent'i kör bir şekilde kontrol eden insanlar hakkında endişeleniyorum ve ardından izleyenler için acı olacak bir dizi sessiz başarısızlık yaratan eksik değerleri görmezden geliyorum.
Winston Ewert,

1
Opsiyonları severim. Fakat şunu söyleyeyim ki, eski boş göstericiler, gerçek dünya kodunda çok nadiren sorun oluyor. Neredeyse her zaman rahatsız edici kod satırının yakınında başarısız olurlar. Ve bence bu konu hakkında konuşurken insanlar tarafından abartılıyor.
LegendLength

@ LegendLength, deneyimim, olguların% 95'inin NULL'un nereden geldiğini tanımlamak için gerçekten izlenebilir olmasıdır. Ancak, kalan% 5'inin izlenmesi oldukça zordur.
Winston Ewert

-1

Objective-C'de tüm nesne referansları isteğe bağlıdır. Bu nedenle, gönderdiğiniz kod aptalca.

if (variable != nil) {
    [variable doThing];
}

Yukarıdaki gibi kod Objective-C'de duyulmamış. Bunun yerine, programcı sadece:

[variable doThing];

Eğer variablebir değer içerir, daha sonra doThingüzerine çağrılır. Olmazsa zarar vermez. Program no-op olarak değerlendirecek ve çalışmaya devam edecektir.

Bu seçeneklerin gerçek yararıdır. Kodunuzu karıştırmak zorunda değilsiniz if (obj != nil).


Bu sadece sessizce başarısız bir form değil mi? Bazı durumlarda, değerin boş olmasına özen gösterebilir ve bu konuda bir şeyler yapmak isteyebilirsiniz.
LegendLength

-3

if (isteğe bağlı.isPresent ()) {

Burada bariz sorun "isteğe bağlı" gerçekten eğer olmasıdır edilmektedir eksik (yani boş) sonra kod Boş bir nesne başvurusu üzerine herhangi bir yöntemi çağırmak için çalışan bir NullReferenceException (veya benzeri) ile havaya uçurmak için gidiyor!

Belirli bir nesne türünün boşluğunu belirleyen statik, Yardımcı sınıf bir yöntem yazmak isteyebilirsiniz, buna benzer bir şey:

function isNull( object o ) 
{
   return ( null == o ); 
}

if ( isNull( optional ) ) ... 

veya, belki de, daha faydalı:

function isNotNull( object o ) 
{
   return ( null != o ); 
}

if ( isNotNull( optional ) ) ... 

Ancak bunlardan biri gerçekten orijinalinden daha okunabilir / anlaşılabilir / sürdürülebilir mi?

if ( null == optional ) ... 
if ( null != optional ) ... 
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.