İsteğe bağlı veya Java'da isteğe bağlı


137

Java 8'deki yeni İsteğe bağlı türle çalışıyorum ve işlevsel olarak desteklenmeyen ortak bir işlem gibi görünen bir şeyle karşılaştım: bir "orElseOptional"

Aşağıdaki modeli düşünün:

Optional<Result> resultFromServiceA = serviceA(args);
if (resultFromServiceA.isPresent) return result;
else {
    Optional<Result> resultFromServiceB = serviceB(args);
    if (resultFromServiceB.isPresent) return resultFromServiceB;
    else return serviceC(args);
}

Bu modelin birçok formu vardır, ancak yeni bir isteğe bağlı üreten bir işlevi alan, yalnızca geçerli olanı yoksa, isteğe bağlı bir "orElse" istemekle kaynar.

Uygulanması şu şekilde görünecektir:

public Optional<T> orElse(Supplier<Optional<? extends T>> otherSupplier) {
    return value != null ? this : other.get();
}

Böyle bir yöntemin var olmamasının bir nedeni olup olmadığını, isteğe bağlı olarak sadece isteğe bağlı olmayan bir şekilde kullanıyorsam ve insanların bu dava ile başa çıkmak için başka hangi yollarla geldiklerini merak ediyorum.

Kodumla çalışan insanlar mutlaka var olduklarını bilmeyeceklerinden, özel yardımcı sınıflar / yöntemler içeren çözümlerin zarif olmadığını düşünüyorum.

Ayrıca, herhangi biri bilirse, böyle bir yöntem JDK 9'a dahil edilir ve böyle bir yöntemi nereden önerebilirim? Bu benim için API için oldukça göze çarpan bir ihmal gibi görünüyor.



Bu! Teşekkürler, aramamda bulamadım.
Yona Appletree

2
@Obicere Bu sorun burada geçerli değildir, çünkü boş bir davranışla ilgilidir İsteğe bağlı, alternatif bir sonuçla ilgili değildir . İsteğe bağlı zaten orElseGet()OP'nin ihtiyacı olan şeylere sahiptir , sadece güzel basamaklı sözdizimi üretmez.
Marko Topolnik


1
Java hakkında harika öğreticiler İsteğe bağlı: codeflex.co/java-optional-no-more-nullpointerexception
John Detroit

Yanıtlar:


86

Bu JDK 9'un ora Supplier<Optional<T>>. Bu durumda örneğin:

return serviceA(args)
    .or(() -> serviceB(args))
    .or(() -> serviceC(args));

Ayrıntılar için Javadoc ya da yazdığım bu gönderiye bakınız .


Güzel. Bu ek bir yaşında olmalı ve ben fark etmedim. Blogunuzdaki soru ile ilgili olarak, bayt kodu çağırma talimatları dönüş türü de dahil olmak üzere tam imzayı ifade ettiğinden, dönüş türünü değiştirmek ikili uyumluluğu bozar, bu nedenle dönüş türünü değiştirme şansı yoktur ifPresent. Ama her neyse, sanırım isim ifPresentiyi değil. Adına taşıyan “başka” değil tüm diğer yöntemler için (gibi map, filter, flatMap), o kadar niye, hiçbir değer varsa onlar hiçbir şey yapmak olduğunu ima edilir ifPresent...
Holger

Bu nedenle Optional<T> perform(Consumer<T> c)zincirlemeye izin verecek bir yöntem eklemek perform(x).orElseDo(y)( orElseDoönerdiğinize alternatif olarak, eksik değerler için bir şeyler yapabilen tüm yöntem adına ifEmptytutarlı olmak else). performJava 9'da stream().peek(x).findFirst()bu API'nın kötüye kullanımı olsa da taklit edebilirsiniz ve aynı anda Runnablebir belirtmeden bir yürütme yolu yoktur Consumer...
Holger

65

Mevcut API verildiğinde en temiz "deneme hizmetleri" yaklaşımı şöyle olacaktır:

Optional<Result> o = Stream.<Supplier<Optional<Result>>>of(
    ()->serviceA(args), 
    ()->serviceB(args), 
    ()->serviceC(args), 
    ()->serviceD(args))
.map(Supplier::get)
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();

Önemli olan, bir kez yazmanız gereken (sabit) işlem zinciri değil, başka bir hizmet eklemenin (veya hizmetlerin listesini genel olarak değiştirmenin ne kadar kolay) olmasıdır. Burada, bir tek eklemek veya kaldırmak ()->serviceX(args)yeterlidir.

Akışların tembel değerlendirmesi nedeniyle, önceki bir hizmet boş olmayan bir değer döndürdüğünde hizmet çağrılmaz Optional.


13
sadece bir projede kullandığımız için şükürler olsun ki kod incelemeleri yapmıyoruz.
Ilya Smagin

3
Bir "orElseGet" zincirinden çok daha temiz, ama aynı zamanda okumak çok daha zor.
slartidan

4
Bu kesinlikle doğrudur ... ama dürüst olmak gerekirse, ayrıştırmam bir saniyemi alır ve bunun doğru olduğundan emin değilim. Dikkat edin, bu örneğin olduğunu anlıyorum, ancak tembel olarak değerlendirilmemesini sağlayacak veya bir bakışta ayırt edilemeyecek başka bir hataya neden olacak küçük bir değişiklik hayal edebiliyorum. Bana göre bu, yararlı bir işlev işlevi olarak iyi olur kategorisine girer.
Yona Appletree

3
Ben .map(Optional::get)ile geçiş .findFirst()"okumayı" kolaylaştıracak mı acaba , örneğin .filter(Optional::isPresent).findFirst().map(Optional::get)"okumak" gibi olabilir akış hangi isteğe bağlı :: isPresent doğrudur ilk öğeyi bulmak ve daha sonra isteğe bağlı :: get "uygulayarak düzleştirmek?
Schatten

3
Komik, birkaç ay önce benzer bir soru için çok benzer bir çözüm yayınladım . Bu ilk karşılaşmam.
shmosel

34

Güzel değil, ama bu işe yarayacak:

return serviceA(args)
  .map(Optional::of).orElseGet(() -> serviceB(args))
  .map(Optional::of).orElseGet(() -> serviceC(args))
  .map(Optional::of).orElseGet(() -> serviceD(args));

.map(func).orElseGet(sup)ile kullanım için oldukça kullanışlı bir modeldir Optional. "Bu Optionaldeğer içeriyorsa vbana ver func(v), yoksa bana versup.get() " .

Bu durumda, biz arar serviceA(args)ve alırız Optional<Result>. Bu Optionaldeğer içeriyorsa v, almak istiyoruz Optional.of(v), ama boşsa, almak istiyoruzserviceB(args) . Daha fazla alternatifle durulayın.

Bu modelin diğer kullanımları

  • .map(Stream::of).orElseGet(Stream::empty)
  • .map(Collections::singleton).orElseGet(Collections::emptySet)

1
huh, bu stratejiyi kullandığımda, eclipse şöyle diyor: "İsteğe bağlı <String> türündeki orElseGet (Tedarikçi <? String>> yöntemi) (() -> {}) argümanları için geçerli değil" Dize bir İsteğe bağlı geçerli bir strateji döndürme düşünün ??
chrismarx

@chrismarx () -> {}bir Optional. Ne yapmaya çalışıyorsun?
Misha

1
Sadece örneği takip etmeye çalışıyorum. Harita çağrılarını bağlamak işe yaramıyor. Hizmetlerim dizeleri döndürüyor ve tabii ki o zaman kullanılabilir .map () seçeneği yok
chrismarx

1
or(Supplier<Optional<T>>)Java 9'un geleceğine mükemmel okunabilir bir alternatif
David M.

1
@Yanıldığın yanlış. .map()boş bir boş Optionalüretecek Optional.
Misha

27

Belki de peşinde olduğun şey budur: Birinden veya başka birinden değer al

Aksi takdirde, bir göz atmak isteyebilirsiniz Optional.orElseGet. İşte benim bir örnek peşinde olduğunu düşündüğüm :

result = Optional.ofNullable(serviceA().orElseGet(
                                 () -> serviceB().orElseGet(
                                     () -> serviceC().orElse(null))));

2
Dahiler genellikle bunu yapar. İsteğe bağlı bir boş değer olarak değerlendirmek ve onu sarma ofNullableşimdiye kadar gördüğüm en havalı şey.
Jin Kwon

5

Hala JDK8'de olduğunuzu varsayarsak, birkaç seçenek vardır.

Seçenek # 1: Kendi Yardımcı Yöntemi Yapın

Örneğin:

public class Optionals {
    static <T> Optional<T> or(Supplier<Optional<T>>... optionals) {
        return Arrays.stream(optionals)
                .map(Supplier::get)
                .filter(Optional::isPresent)
                .findFirst()
                .orElseGet(Optional::empty);
    }
}

Böylece şunları yapabilirsiniz:

return Optionals.or(
   ()-> serviceA(args),
   ()-> serviceB(args),
   ()-> serviceC(args),
   ()-> serviceD(args)
);

Seçenek # 2: Kütüphane Kullanın

Google guava's Optional isteğe bağlı olarak düzgün bir or()işlemi destekler (JDK9 gibi), örneğin:

return serviceA(args)
  .or(() -> serviceB(args))
  .or(() -> serviceC(args))
  .or(() -> serviceD(args));

(Hizmetlerin her birinin com.google.common.base.Optionalyerine geri döndüğü yer java.util.Optional).


Optional<T>.or(Supplier<Optional<T>>)Guava belgelerinde bulamadım . Bunun için bir bağlantın var mı?
Tamas Hegedus

.orElseGet T değerini döndürür ancak Optional :: empty İsteğe bağlı <T> değerini döndürür. İsteğe bağlı.Nulllable (........ veyaElse (null)), @aioobe tarafından açıklanan şekilde istenen etkiye sahiptir.
Miguel Pereira

@TamasHegedus 1. seçenekte mi demek istediniz? Üstündeki parçacığın üzerinde bulunan özel bir uygulamadır. ps: geç cevap için üzgünüm
Sheepy

@MiguelPereira .orElseGet bu durumda İsteğe bağlı <T> döndürür, çünkü İsteğe bağlı <İsteğe bağlı <T>> üzerinde çalışır
Sheepy

2

Bu desen eşleşmesi için iyi bir uyum ve bazı ile daha geleneksel bir seçenek arayüzü ve (örneğin içinde olanlar gibi Yok uygulamaları gibi görünüyor olduğunu Javaslang , FunctionalJava ) ya da tembel Belki de uygulama tepki Cyclops .Çok bu kütüphaneye yazarı.

İle Tepegöz-tepki de yapısal kullanabilirsiniz modeli eşleşmesi JDK türlerinde. İsteğe bağlı olarak, mevcut ve olmayan vakaları ziyaretçi kalıbı üzerinden eşleştirebilirsiniz . şöyle bir şey olurdu -

  import static com.aol.cyclops.Matchables.optional;

  optional(serviceA(args)).visit(some -> some , 
                                 () -> optional(serviceB(args)).visit(some -> some,
                                                                      () -> serviceC(args)));
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.