Java 8 - Optional.flatMap ve Optional.map arasındaki fark


162

Bu iki yöntem arasındaki fark nedir: Optional.flatMap()ve Optional.map()?

Bir örnek takdir edilecektir.



5
@AlexisC. Bağlantınız Stream'in haritası ve flatMap ile ilgilidir, İsteğe bağlı değil.
Eran

1
@Eran Bu önemli değil, map / flatMap'in bir Akış için olsun veya olmasın nasıl çalıştığını anlarsanız, İsteğe Bağlı için de aynıdır. Operatör bir Akış için nasıl çalıştığını anladıysa, bu soruyu sormamalı. Kavram aynı.
Alexis C.

2
@AlexisC. Pek sayılmaz. İsteğe bağlı flatMap'in Stream'in flatMap ile pek bir ortak yanı yoktur.
Eran

1
@Eran Bir harita ve flatMap arasındaki kavramsal farktan bahsediyorum, Stream#flatMapve ile arasında birebir karşılık vermem Optional#flatMap.
Alexis C.

Yanıtlar:


166

Kullanım mapişlevi gerekmez nesne veya dönerse flatMapişlevini verir eğer bir Optional. Örneğin:

public static void main(String[] args) {
  Optional<String> s = Optional.of("input");
  System.out.println(s.map(Test::getOutput));
  System.out.println(s.flatMap(Test::getOutputOpt));
}

static String getOutput(String input) {
  return input == null ? null : "output for " + input;
}

static Optional<String> getOutputOpt(String input) {
  return input == null ? Optional.empty() : Optional.of("output for " + input);
}

Her iki yazdırma ifadesi de aynı şeyi yazdırır.


5
Soru: [flat]MapHarita işlevini input == nullhiç? Anladığım kadarıyla, Optionalsıralama yoksa, [JavaDoc] ( docs.oracle.com/javase/8/docs/api/java/util/… ) bunu destekliyor gibi görünüyor - " Bir değer varsa, uygulayın .. . "
Örümcek Boris

1
@BoristheSpider Optional.of (null)! = Optional.empty ()
Diego Martinoia

14
@DiegoMartinoia Optional.of(null)bir Exception. Optional.ofNullable(null) == Optional.empty().
Örümcek Boris

1
@BoristheSpider evet, haklısın. Sorunuza cevap vermeye çalışıyordum ama daha da belirsiz hale getirdiğimi düşünüyorum: kavramsal olarak, Optional.ofNullable (null) boş olmamalı, ancak pratikte olduğu düşünülüyor ve bu nedenle harita / flatmap yürütülmüyor.
Diego Martinoia

1
Bence getOutputOpt veya getOutput'ta giriş asla boş olmamalı
17'de DanyalBurke

55

Her ikisi de isteğe bağlı türden bir şeye bir işlev alır.

map()isteğe bağlı olarak " olduğu gibi " işlevini uygular :

if (optional.isEmpty()) return Optional.empty();
else return Optional.of(f(optional.get()));

İşleviniz bir işlevse ne olur T -> Optional<U>?
Sonuç şimdi bir Optional<Optional<U>>!

İşte bununla flatMap()ilgili: eğer fonksiyonunuz zaten bir döndürürse Optional, flatMap()biraz daha zekidir ve onu çift sarmaz, geri döner Optional<U>.

İki işlevsel deyimin bileşimidir: mapve flatten.


7

Not: - aşağıda harita ve fistingp işlevinin gösterimi verilmiştir, aksi takdirde İsteğe bağlı olarak yalnızca bir dönüş türü olarak kullanılmak üzere tasarlanmıştır.

Bildiğiniz gibi İsteğe bağlı, tek bir nesne içerebilen veya içermeyen bir tür kapsayıcıdır, bu nedenle boş bir değer beklediğiniz her yerde kullanılabilir (İsteğe bağlı olarak düzgün kullanılırsa hiçbir zaman NPE'yi göremezsiniz). Örneğin, boş olabilecek bir kişi nesnesini bekleyen bir yönteminiz varsa, yöntemi şöyle bir şey yazmak isteyebilirsiniz:

void doSome(Optional<Person> person){
  /*and here you want to retrieve some property phone out of person
    you may write something like this:
  */
  Optional<String> phone = person.map((p)->p.getPhone());
  phone.ifPresent((ph)->dial(ph));
}
class Person{
  private String phone;
  //setter, getters
}

Burada otomatik olarak İsteğe bağlı bir türe sarılmış bir String türü döndürdünüz.

Kişi sınıfı buna benziyorsa, yani telefon da isteğe bağlıdır

class Person{
  private Optional<String> phone;
  //setter,getter
}

Bu durumda, harita işlevini çağırmak döndürülen değeri İsteğe bağlı olarak sarar ve şöyle bir şey verir:

Optional<Optional<String>> 
//And you may want Optional<String> instead, here comes flatMap

void doSome(Optional<Person> person){
  Optional<String> phone = person.flatMap((p)->p.getPhone());
  phone.ifPresent((ph)->dial(ph));
}

PS; NullPointerExceptions olmadan yaşayamazsanız, hiçbir zaman isteğe bağlı olarak isPresent () ile kontrol etmeden get yöntemini (gerekirse) çağırmayın.


1
Bence bu örnek cevabınızın doğasından Personuzaklaşacak gibi görünüyor çünkü sınıfınız yanlış kullanıyor Optional. API'nin Optionalbu gibi üyelerde kullanılması niyeti karşı - bkz. Mail.openjdk.java.net/pipermail/jdk8-dev/2013-Eylül/…
8bitjunkie

@ 8bitjunkie Bunu işaret ettiğiniz için teşekkürler, Scala'nın Seçeneğinden farklı ..
SandeepGodara

6

Bana yardımcı olan iki fonksiyonun kaynak koduna bir göz atmaktı.

Harita - sonucu İsteğe bağlı olarak sarar.

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Optional.ofNullable(mapper.apply(value)); //<--- wraps in an optional
    }
}

flatMap - 'raw' nesnesini döndürür

public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Objects.requireNonNull(mapper.apply(value)); //<---  returns 'raw' object
    }
}

1
flatMap"Ham" nesneyi döndürür "ile ne demek istiyorsun ? flatMapayrıca eşlenen nesneyi bir Optional. Fark durumunda olmasıdır flatMapeşleştirici fonksiyonu eşlenen nesne kaydırılır Optionalise mapkendi içinde bir nesne sarar Optional.
Derek Mahar

@DerekMahar benimkini sildi, yeniden göndermenize gerek yok, çünkü yorumunuzu doğru düzenlediniz.
maxxyme

3
  • Optional.map():

Her öğeyi alır ve değer varsa işleve aktarılır:

Optional<T> optionalValue = ...;
Optional<Boolean> added = optionalValue.map(results::add);

Şimdi eklenen üç değerden birine sahiptir: trueveya varsa falsebir İsteğe bağlıoptionalValue veya boş bir İsteğe bağlı içine sarılmış .

Kullanabileceğiniz sonucu işlemeniz ifPresent()gerekmiyorsa, dönüş değeri yoktur:

optionalValue.ifPresent(results::add); 
  • Optional.flatMap():

Aynı akış yöntemine benzer şekilde çalışır. Akarsu akışını düzleştirir. Fark, eğer değer sunuluyorsa fonksiyona uygulanır. Aksi takdirde, boş bir isteğe bağlı döndürülür.

İsteğe bağlı değer işlevleri çağrıları oluşturmak için kullanabilirsiniz.

Yöntemlerimiz olduğunu varsayalım:

public static Optional<Double> inverse(Double x) {
    return x == 0 ? Optional.empty() : Optional.of(1 / x);
}

public static Optional<Double> squareRoot(Double x) {
    return x < 0 ? Optional.empty() : Optional.of(Math.sqrt(x));
}

Sonra tersin karekökünü hesaplayabilirsiniz, örneğin:

Optional<Double> result = inverse(-4.0).flatMap(MyMath::squareRoot);

veya isterseniz:

Optional<Double> result = Optional.of(-4.0).flatMap(MyMath::inverse).flatMap(MyMath::squareRoot);

Ya olursa inverse()veya squareRoot()döner Optional.empty(), sonuç boş.


1
Bu derlenmez. İfadelerinizin her ikisi de, sonucu atadığınız İkili yerine İsteğe Bağlı bir <Double> döndürür.
JL_SO

@JL_SO haklısın. Çünkü tersi Optional<Double>dönüş türü olarak tipe sahiptir.
nazar_art

3

Tamam. Sen iç içe geçmiş Opsiyoneller karşı karşıya olduğunuzda sadece kullanım 'flatMap' gerek . İşte örnek.

public class Person {

    private Optional<Car> optionalCar;

    public Optional<Car> getOptionalCar() {
        return optionalCar;
    }
}

public class Car {

    private Optional<Insurance> optionalInsurance;

    public Optional<Insurance> getOptionalInsurance() {
        return optionalInsurance;
    }
}

public class Insurance {

    private String name;

    public String getName() {
        return name;
    }

}

public class Test {

    // map cannot deal with nested Optionals
    public Optional<String> getCarInsuranceName(Person person) {
        return person.getOptionalCar()
                .map(Car::getOptionalInsurance) // ① leads to a Optional<Optional<Insurance>
                .map(Insurance::getName);       // ②
    }

}

Akış gibi, İsteğe bağlı # harita İsteğe bağlı tarafından sarılmış bir değer döndürür. Bu yüzden iç içe bir isteğe bağlı oluruz Optional<Optional<Insurance>. Ve ② olarak, bunu bir Sigorta örneği olarak eşlemek istiyoruz, bu trajedi böyle oldu. Kök yuvalanmış Optionals. Kabuklardan bağımsız olarak temel değeri alabilirsek, halledeceğiz. FlatMap bunu yapar.

public Optional<String> getCarInsuranceName(Person person) {
    return person.getOptionalCar()
                 .flatMap(Car::getOptionalInsurance)
                 .map(Insurance::getName);
}

Sonunda, Java8 Sistematik olarak çalışmak istiyorsanız, Java 8 In Action'ı size şiddetle tavsiye ettim.

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.