Geleneksel boş işaretçi denetimleri yerine neden Java 8+ sürümünde İsteğe Bağlı kullanılır?


110

Geçenlerde Java 8'e geçtik. Şimdi, Optionalnesnelerle dolu uygulamaları görüyorum .

Java 8'den Önce (Stil 1)

Employee employee = employeeServive.getEmployee();

if(employee!=null){
    System.out.println(employee.getId());
}

Java 8'den Sonra (Stil 2)

Optional<Employee> employeeOptional = Optional.ofNullable(employeeService.getEmployee());
if(employeeOptional.isPresent()){
    Employee employee = employeeOptional.get();
    System.out.println(employee.getId());
}

Optional<Employee> employeeOptional = employeeService.getEmployee();Hizmetin kendisinin isteğe bağlı olarak döndüğü zaman hiçbir katma değer göremiyorum :

Bir Java 6 arka planından gelince, Stil 1'i daha net ve daha az kod satırıyla görüyorum. Burada kaçırdığım herhangi bir gerçek avantaj var mı?

Tüm cevapların anlaşılmasından ve blogda daha fazla araştırma yapılmasından konsolide



28
Yuck! employeeOptional.isPresent()seçenek türleri noktasını tamamen eksik gibi görünüyor. @ MikePartridge'in yorumuna göre, bu önerilen veya aptal değildir. Bir seçenek türünün bir şey ya da hiçbir şey olup olmadığını açıkça kontrol etmek , hayır-hayır olduğunu gösterir. Basitçe mapveya flatMapüzerlerinden geçmen gerekiyordu .
Andres F.

7
Bkz Bir yığın taşması üzerinde YanıtOptional tarafından Brian Goetz , Java Dil Mimar Oracle'da.
Basil Bourque

6
Beyninizi "en iyi uygulamalar" adına kapattığınızda olan budur.
immibis

9
Bu isteğe bağlı olarak oldukça zayıf bir kullanımdır ancak bunun faydaları da vardır. Boş değerlerde, her şey boş olabilir, bu nedenle sürekli olarak kontrol etmeniz gerekir, isteğe bağlı olarak bu özel değişkenin eksik olduğunu söyler, kontrol ettiğinizden emin olun
Richard Tingle

Yanıtlar:


108

Stil 2, tam yararı görecek kadar Java 8'e gitmiyor. Sen hiç istemiyorsun if ... use. Oracle'ın örneklerine bakın . Onların tavsiyelerine göre, biz:

Stil 3

// Changed EmployeeServive to return an optional, no more nulls!
Optional<Employee> employee = employeeServive.getEmployee();
employee.ifPresent(e -> System.out.println(e.getId()));

Veya daha uzun bir snippet

Optional<Employee> employee = employeeServive.getEmployee();
// Sometimes an Employee has forgotten to write an up-to-date timesheet
Optional<Timesheet> timesheet = employee.flatMap(Employee::askForCurrentTimesheet); 
// We don't want to do the heavyweight action of creating a new estimate if it will just be discarded
client.bill(timesheet.orElseGet(EstimatedTimesheet::new));

19
gerçekten de isPresent'in (kod incelemesi tarafından yakalanan) çoğu kullanımını yasaklıyoruz
jk.

10
@jk. gerçekten, eğer bir aşırı yük void ifPresent(Consumer<T>, Runnable)olsaydı, toplam bir yasağı için tartışırdım isPresentveget
Caleth

10
@Caleth: Java 9'lar ilginizi çekebilir ifPresentOrElse(Consumer<? super T>, Runnable).
wchargin

9
Java 8 stiline göre Java 6 ile karşılaştırıldığında bir dezavantajı buluyorum: kod kapsamı araçlarını aptal. İf ifadesi ile burada değil, bir şubeyi ne zaman kaçırdığınızı gösterir.
Thierry

14
@Aaron "Kod okunabilirliğini büyük ölçüde azaltır". Hangi kısım okunabilirliği tam olarak azaltır? “Her zaman farklı bir şekilde yaptım ve alışkanlıklarımı değiştirmek istemiyorum” nesnel akıl yürütmekten ziyade.
Voo

47

OptionalHala dönebilecek eski bir API arasında "uyumluluk" katmanı olarak kullanıyorsanız null, en son aşamada bir şeyiniz olduğundan emin olduğunuz (boş olmayan) İsteğe Bağlı bir seçeneği oluşturmak yararlı olabilir . Örneğin, yazdığınız yer:

Optional<Employee> employeeOptional = Optional.ofNullable(employeeService.getEmployee());
if(employeeOptional.isPresent()){
    Employee employeeOptional= employeeOptional.get();
    System.out.println(employee.getId());
}

Ben doğru tercih ederim:

Optional.of(employeeService)                 // definitely have the service
        .map(EmployeeService::getEmployee)   // getEmployee() might return null
        .map(Employee::getId)                // get ID from employee if there is one
        .ifPresent(System.out::println);     // and if there is an ID, print it

Nokta olduğunu biliyoruz boş olmayan bir çalışanın var ki servis , buna bağlı olarak bir o paketleyebilirim Optionalile Optional.of(). Daha sonra, onu aradığınızda getEmployee(), bir çalışanınız olabilir veya olmayabilir. Bu çalışanın kimliği olabilir (veya olabilir). Daha sonra bir kimliğiniz olursa, yazdırmak istersiniz.

Açıkça kontrol etmek gerek yoktur herhangi Bu kodda, vb boş, varlığı.


4
(...).ifPresent(aMethod)Üzerinden kullanmanın avantajı nedir if((...).isPresent()) { aMethod(...); }? Hemen hemen aynı işlemi yapmak için bir başka sözdizimi gibi görünüyor.
Ruslan

2
@Ruslan Neden tüm durumlar için eşit derecede iyi çalışan bir genel çözümünüz varken, belirli bir arama için özel bir sözdizimi kullanıyorsunuz? Aynı fikri burada haritadan ve co'dan da uyguluyoruz.
Voo

1
@Ruslan ... bir API, boş koleksiyonlar veya diziler yerine null değer döndürür. : Ör, gibi bir şey (çocukları varsa GetChildren () bir çocuğun setini veya null döndürür kendisi için Veli sınıfını varsayılarak) yapabileceği Set<Children> children = Optional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet); Şimdi işleyebilir children, örneğin eşit children.forEach(relative::sendGift);. Bunlar bile kombine edilebilir: Optional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet).forEach(relative::sendGift);. Boş kontrol, vb. Tüm kazanları ...
Joshua Taylor

1
@Ruslan ... standart kütüphanede denenmiş ve gerçek kod için yetkilendirilmiştir. Yani vb Ben bir programcı olarak, yazmak zorunda kod test, hata ayıklama miktarını azaltır
Joshua Taylor

1
Swift kullandığıma sevindim. e izin verirseniz, çalışan yüz yüzu = yüzen_Service.employee {print (worker.Id); }
gnasher729

23

OptionalTek bir değere sahip olmanın katma değeri yoktur . Gördüğünüz gibi, bu sadece nullvarlığını kontrol eden bir çekin yerine geçer .

Bunlardan birine sahip olmanın çok büyük katma değeri var Collection. Eski stil düşük seviye döngülerinin yerini almak üzere sunulan tüm akış yöntemleri bir Optionalşeyleri anlar ve bunlar hakkında doğru olanı yapar (onları işleme koymayıp sonuçta başka bir küme döndürmez Optional). N öğelerini tek tek öğelere kıyasla (ve tüm gerçekçi programların% 99'unu yaparsanız) daha fazla ele alırsanız , isteğe bağlı olarak sunulan şeylere yönelik yerleşik desteğe sahip olmak büyük bir gelişmedir. İdeal durumda, hiçbir zaman kontrol etmeniz null veya varlığınız olması gerekmez .


24
Kenara koleksiyonları / derelerden, bir Opsiyonel API'ler daha çok kendine documenting, örneğin yapmak da büyük olur Employee getEmployee()vs Optional<Employee> getEmployee(). Teknik olarak ikisi de geri dönebilse de null, bunlardan birinin sorun çıkarması çok daha muhtemeldir.
amon

16
Tek bir değerin isteğe bağlı olarak bol miktarda değeri vardır. .map()Tüm yol boyunca boş kontrol etmek zorunda kalmadan olmak harika. Örneğin, Optional.of(service).map(Service::getEmployee).map(Employee::getId).ifPresent(this::submitIdForReview);.
Joshua Taylor

9
Katma değer olmayan ilk yorumunuza katılmıyorum. İsteğe bağlı, kodunuza içerik sağlar. Hızlı bir bakış size bir fonksiyonun doğruluğunu düzeltebilir ve tüm vakalar kapsanabilir. Boş denetimde ise, her bir yöntemdeki her bir Referans türünü boş denetlemelisiniz? Benzer bir durum Sınıflar ve kamu / özel değiştiriciler için de geçerlidir; Kodunuza sıfır değer katıyorlar, değil mi?
18:18

3
@JoshuaTaylor Dürüst olmak gerekirse, bu ifadeye kıyasla , eski kafalı çekimi tercih ediyorum null.
Kilian Foth

2
İsteğe bağlı olarak tek değerlerle bile bir diğer büyük avantaj, ne zaman yapmanız gerektiğini belirlemek için boşluğu kontrol etmeyi unutmamaktır. Aksi halde çekleri unutmak ve NullPointerException ile son vermek çok kolaydır ...
Sean Burton

17

OptionalSadece bir fantezi API gibi kullandığınız sürece isNotNull(), evet, sadece kontrol ederken hiçbir fark bulamazsınız null.

Eğer KULLANMAYIN gerekenler Optionaliçin

OptionalSadece bir değerin varlığını kontrol etmek için kullanma Bad Code ™:

// BAD CODE ™ --  just check getEmployee() != null  
Optional<Employee> employeeOptional =  Optional.ofNullable(employeeService.getEmployee());  
if(employeeOptional.isPresent()) {  
    Employee employee = employeeOptional.get();  
    System.out.println(employee.getId());
}  

Kullanmanız gereken şeyler Optionaliçin

Null döndüren API yöntemleri kullanılırken gerektiğinde bir tane üreterek, mevcut olmayan bir değerden kaçının:

Employee employee = Optional.ofNullable(employeeService.getEmployee())
                            .orElseGet(Employee::new);
System.out.println(employee.getId());

Veya, yeni bir Çalışan oluşturmak çok pahalıysa:

Optional<Employee> employee = Optional.ofNullable(employeeService.getEmployee());
System.out.println(employee.map(Employee::getId).orElse("No employee found"));

Ayrıca, herkesi yöntemlerin bir değer getirmeyebileceğinin farkına varma (eğer, hangi nedenle olursa olsun yukarıdaki gibi bir varsayılan değer döndüremezseniz):

// Your code without Optional
public Employee getEmployee() {
    return someCondition ? null : someEmployee;
}
// Someone else's code
Employee employee = getEmployee(); // compiler doesn't complain
// employee.get...() -> NPE awaiting to happen, devs criticizing your code

// Your code with Optional
public Optional<Employee> getEmployee() {
    return someCondition ? Optional.empty() : Optional.of(someEmployee);
}
// Someone else's code
Employee employee = getEmployee(); // compiler complains about incompatible types
// Now devs either declare Optional<Employee>, or just do employee = getEmployee().get(), but do so consciously -- not your fault.

O gerçekten olmasalar da Ve son olarak, zaten başka cevaplar açıklandığı olan tüm akışı benzeri yöntemler var sen kullanmaya karar Optionalancak yararlanarak Optionalbaşkası tarafından sağlanan.


1
@Kepep Gerçekten. Bu tartışmayı / r / java'da yaptık ve şöyle dedim : « OptionalKaçırılmış bir fırsat olarak görüyorum . “Burada, Optionalyaptığım bu yeni şeyi yaptım, böylece boş değerleri kontrol etmek zorundasın. Oh, ve bu arada ... Ona bir get()yöntem ekledim, böylece aslında hiçbir şey kontrol etmen gerekmez;);)” . (...) Optional"BU NULL OLABİLİR" neon tabelası olarak güzeldir, ancak get()yönteminin çıplak varlığı onu sakat tutar » . Fakat OP, nedenlerini Optionaldeğil, nedenlerini değil, kusurlarını tasarlamasını istedi.
Walen

1
Employee employee = Optional.ofNullable(employeeService.getEmployee());Üçüncü snippet'inizde tür olmamalı Optional<Employee>mı?
Charlie Harding

2
@ immibis Ne şekilde sakatlandı? Kullanmanın temel nedeni get, bazı eski kodlarla etkileşime geçmek zorunda olmanızdır. Şimdiye kadar kullanmak için yeni kodda birçok geçerli nedenleri düşünemiyorum get. Eski kodla etkileşim kurarken genellikle alternatifin olmadığını, ancak hala walen'ın getyeni başlayanların kötü kod yazmasına neden olduğu bir noktaya sahip olduğunu söyledi .
Voo

1
@ immibis Bir Mapkeresinde bahsetmedim ve geri dönüşü olmayan, uyumlu bir değişiklik yapan çok ciddi bir kimsenin farkında değilim, bu yüzden kasıtlı olarak kötü API'ler tasarlayabildiğinizden Optionalemin olun - bunun neyi kanıtladığından emin değilsiniz. Bir olsa Optionalbazı tryGetyöntem için mantıklı olur .
Voo

2
@Voo Mapherkesin aşina olduğu bir örnektir. Tabii ki Map.getgeriye dönük uyumluluk nedeniyle bir Opsiyonel geri dönüş yapamazlar , ancak böyle bir uyumluluk kısıtlaması olmayan başka Harita benzeri bir sınıfı kolayca hayal edebilirsiniz. Söyle class UserRepository {Optional<User> getUserDetails(int userID) {...}}. Şimdi, oturum açmış kullanıcının ayrıntılarını almak istiyorsunuz ve oturum açmayı başardıkları için sisteminiz kullanıcıları silmiyor çünkü onların var olduğunu biliyorsunuz. Yani sen theUserRepository.getUserDetails(loggedInUserID).get().
immibis,

12

İsteğe Bağlı seçeneğini kullanmamda kilit nokta, geliştiricinin işlev dönüş değeri olup olmadığını kontrol etmesi gerektiğinde açık tutmaktır.

//service 1
Optional<Employee> employeeOptional = employeeService.getEmployee();
if(employeeOptional.isPresent()){
    Employee employeeOptional= employeeOptional.get();
    System.out.println(employee.getId());
}

//service 2
Employee employee = employeeService.getEmployeeXTPO();
System.out.println(employee.getId());

8
Opsiyonel olan tüm şık fonksiyonel şeylerin, olması gerçekten güzel olmasına rağmen, buradaki noktadan daha önemli olarak görülmesi beni şaşırtıyor. Java 8'den önce, bir çağrıdan gelen yanıtı null kontrol edip etmediğinizi bilmek için Javadoc'u araştırmanız gerekiyordu. Java 8'i geri gönderirseniz, geri dönüş türünden "hiçbir şey" elde edip edemediğinizi bilirsiniz.
Buhb

3
Eh, işlerin hala boş döndüğü "eski kod" sorunu var ... ama evet, imzadan söyleyebilmek harika.
Haakon Løtveit

5
Evet, kesinlikle isteğe bağlı <T> 'nin , bir değerin eksik olabileceğini ifade etmenin tek yolu olduğu, yani null ptr olmayan diller için daha iyi çalıştığını gösterir .
dureuill

8

Asıl hata, hala daha usule uygun şekilde düşündüğün. Bu bir kişi olarak sizi eleştirmek anlamına gelmez, sadece bir gözlemdir. Daha işlevsel terimlerle düşünmek zaman ve pratikle gelir ve bu nedenle bu yöntemler sizin için en açık ve doğru şeylere benziyor. İkincil küçük hata ise, İsteğe Bağlı yönteminizi yönteminizde oluşturmaktır. İsteğe Bağlı, bir şeyin bir değer getirebileceğini veya vermeyeceğini belgelemeye yardımcı olmak içindir. Hiçbir şey alamayabilirsin.

Bu, size makul derecede makul görünen mükemmel okunaklı kodlar yazmanıza neden oldu, ancak aldığınız ve isPresent olan aşağılık ikiz baştan çıkarıcıları tarafından baştan çıktınız.

Tabii ki hızlı bir şekilde soru, "Neden Hazır ve Oraya Gidiyor?"

Buradaki birçok kişinin özlediği şey şudur: isPresent (), insanlar tarafından gemide yazılan yeni kod için yapılmış olan ve faydalı lambdaların ne kadar işe yaradığı ve işlevsel olanı sevenler için yapılan bir şey değildir.

Ancak, bize birkaç (iki) iyi, harika, çekici (?) Yarar sağlar:

  • Yeni özellikleri kullanmak için eski kod geçişini kolaylaştırır.
  • İsteğe bağlı öğrenme eğrilerini kolaylaştırır.

İlki oldukça basittir.

Buna benzeyen bir API'niz olduğunu hayal edin:

public interface SnickersCounter {
  /** 
   * Provides a proper count of how many snickers have been consumed in total.
   */
  public SnickersCount howManySnickersHaveBeenEaten();

  /**
    * returns the last snickers eaten.<br>
    * If no snickers have been eaten null is returned for contrived reasons.
    */
  public Snickers lastConsumedSnickers();
}

Ve bunu kullanarak eski bir sınıfınız vardı (boşlukları doldurun):

Snickers lastSnickers = snickersCounter.lastConsumedSnickers();
if(null == lastSnickers) {
  throw new NoSuchSnickersException();
}
else {
  consumer.giveDiabetes(lastSnickers);
}

Emin olmak için kesin bir örnek. Ama burada benimle ayı.

Java 8 şimdi başlattı ve gemiye girmek için çabalıyoruz. Dolayısıyla yaptığımız şeylerden biri, eski arayüzümüzü İsteğe Bağlı olan bir şeyle değiştirmek istediğimizdir. Neden? Çünkü başkası nezaketle bahsetmişken: Bu, bir şeyin null olabileceği ya da olmama tahminini ortadan kaldırır. Bu, başkaları tarafından çoktan işaret edilmiştir. Ama şimdi bir sorunumuz var. (Masum bir yöntemde alt + F7'ye çarptığım için afedersiniz), bu yöntemin 46 mükemmel şekilde işin iyi yapıldığını söyleyen eski kodda çağrıldığı bir yer olduğunu hayal edin. Şimdi bunların hepsini güncellemelisiniz.

BU, Present'in parladığı yerdir.

Çünkü şimdi: Snickers lastSnickers = snickersCounter.lastConsumedSnickers (); if (null == lastSnickers) {yeni NoS BusesnickersException () at; } else {consumer.giveDiabetes (lastSnickers); }

dönüşür:

Optional<Snickers> lastSnickers = snickersCounter.lastConsumedSnickers();
if(!lastSnickers.isPresent()) {
  throw new NoSuchSnickersException();
}
else {
  consumer.giveDiabetes(lastSnickers.get());
}

Ve bu yeni küçük çocuğa verebileceğiniz basit bir değişiklik: O yararlı bir şeyler yapabilir ve aynı zamanda kod tabanını da keşfedecektir. kazan-kazan. Sonuçta, bu stile benzer bir şey oldukça yaygın. Ve şimdi, lambda veya başka bir şey kullanmak için kodu yeniden yazmak zorunda değilsiniz. (Bu özel durumda önemsiz olurdu, ancak okuyucunun alıştırması yapmanın zor olacağı örnekleri düşünerek bırakıyorum.)

Bunun, sizin yaptığınız yolun, esasen pahalı yeniden yazma yapmadan eski kodlarla başa çıkmanın bir yolu olduğu anlamına geldiğine dikkat edin. Peki ya yeni kod?

Durumunda, sadece bir şeyi basmak istediğin yerde, yapardın:

. SnickersCounter.lastConsumedSnickers () ifPresent (System.out :: println);

Hangi oldukça basit ve tamamen açık. O zaman yavaşça yüzeye sıçrayan nokta, get () ve isPresent () için kullanım durumları olduğudur. Daha fazla düşünmeden yeni türleri kullanmak için var olan kodu mekanik olarak değiştirmenize izin vermek için oradalar. Yaptıklarınız bu nedenle aşağıdaki şekillerde yanlış yönlendirilmişlerdir:

  • Siz null dönebilecek bir yöntem çağırıyorsunuz. Doğru fikir, yöntemin null değerini döndürdüğüdür.
  • Bu isteğe bağlı ile baş etmek için miras bandajı yöntemleri içeren lezzetli yeni yöntemleri kullanmak yerine eski bandaj yöntemlerini kullanıyorsunuz.

Optional'ı basit bir sıfır güvenlik kontrolü olarak kullanmak istiyorsanız, yapmanız gereken şey şudur:

new Optional.ofNullable(employeeServive.getEmployee())
    .map(Employee::getId)
    .ifPresent(System.out::println);

Tabii ki, bu iyi görünümlü versiyonu gibi görünüyor:

employeeService.getEmployee()
    .map(Employee::getId)
    .ifPresent(System.out::println);

Bu arada, hiçbir zorunluluk olmasa da, okuma başına daha kolay olması için işlem başına yeni bir satır kullanmanızı öneririm. Okuması ve anlaşılması kolay, haftanın herhangi bir günü özlülük yener.

Elbette, yapmaya çalıştığımız her şeyi anlamanın kolay olduğu çok basit bir örnek. Gerçek hayatta her zaman bu kadar basit değildir. Ancak, bu örnekte, ifade ettiğimiz şeyin niyetlerimizin ne kadar olduğuna dikkat edin. Çalışanı almak, kimliğini almak ve mümkünse yazdırmak istiyoruz. Bu, Optional ile ikinci büyük galibiyet. Daha net bir kod oluşturmamızı sağlar. Ayrıca bir harita üzerinde besleyebilmeniz için bir sürü şey yapan bir yöntem yapmak gibi şeyler yapmanın genel olarak iyi bir fikir olduğunu düşünüyorum.


1
Buradaki problem, bu işlevsel versiyonlar gibi eski versiyonlar kadar okunaklı gibi görünmesini sağlamaya çalışmanızdır, bir durumda bir örneğin "tamamen açık" olduğu söylenebilir. Durum böyle değil. Bu örnek açıktı, ancak tam olarak değil, daha fazla düşünce gerektirdiği için. map(x).ifPresent(f)basitçe aynı sezgisel duyguyu anlamıyor if(o != null) f(x). ifVersiyon çok açıktır. Bu, popüler bir grup vagonuna atlayan insanların bir başka olgusudur, * değil * gerçek bir gelişme.
Aaron

1
Fonksiyonel programlama veya kullanımında sıfır fayda olmadığını söylemiyorum Optional. Bu özel durumda sadece isteğe bağlı kullanımdan bahsediyordum ve okunabilirliğe daha fazla değindim, çünkü bu format gibi davranan birden fazla kişi eşit veya daha fazla okunabilir if.
Aaron

1
Bu açıklık anlayışımıza bağlıdır. Çok iyi puanlar verirken, ben de birçok insan için lambdaların bir noktada yeni ve garip olduğunu belirtmek isterim. Epeyce insanın isPresent dediği ve hoş bir rahatlama hissettiğini söyleyen bir internet çerezi oynamaya cüret ederdim: Artık eski kodu güncellemeye devam edebiliyorlar ve daha sonra lambda sözdizimini öğreniyorlardı. Öte yandan, Lisp, Haskell ya da benzer dil deneyimine sahip insanlar, şimdi
java'daki

@Aaron - netlik Opsiyonel tiplerin noktası değildir. Şahsen netliği azaltmadıklarını görüyorum ve bazı durumlarda (bu durumda olmasa da) arttırdıkları, ancak asıl yararı (kullanmaktan kaçındığınız sürece isPresentve getgerçekçi olarak mümkün olduğu sürece ) iyileştirmek güvenliği . Tür Optional<Whatever>bir nulble kullanmak yerine , değerin yokluğunu kontrol edemeyen yanlış kod yazmak daha zordur Whatever.
Jules

Hemen değerlerini çıkarsak bile, sürece kullanmayın olarakget , bir eksik değer oluştuğunda ne yapacağını seçim yapmak zorunda olduğunuz: kullanırsınız ya orElse()(ya orElseGet()) bir varsayılan tedarik etmek veya orElseThrow()bir atmak seçilmiş Bunun istisnası , yığın izini kazana kadar anlamsız olan genel bir NPE'den daha iyi olan, sorunun doğasını belgeleyen bir istisnadır .
Jules

0

Stil 2'de fayda göremiyorum. Boş denetime ihtiyacınız olduğunu ve şimdi daha da büyük, dolayısıyla daha az okunabilir olduğunu fark etmeniz gerekiyor.

WorkerServive.getEmployee (), Optional öğesini döndürür ve ardından kod şöyle olur: Daha iyi bir stil olurdu.

Optional<Employee> employeeOptional = employeeServive.getEmployee();
if(employeeOptional.isPresent()){
    Employee employee= employeeOptional.get();
    System.out.println(employee.getId());
}

Bu şekilde callemployeeServive.getEmployee (). GetId () yöntemini kullanamazsınız ve bir şekilde isteğe bağlı valoue almanız gerektiğini fark edersiniz. Ve tüm kod temeli metotlarınızda asla boşa dönmezseniz (ya da bu kuralın istisnaları dışında ekibiniz tarafından iyi bilinmediğinde), NPE'ye karşı güvenliği artıracaktır.


12
İsteğe bağlı, kullandığınız anda anlamsız bir komplikasyon haline gelir isPresent(). İsteğe bağlı kullanıyoruz, böylece test etmek zorunda kalmıyoruz.
candied_orange

2
Diğerlerinin dediği gibi kullanmayın isPresent(). Bu seçeneklere yanlış bir yaklaşımdır. Kullan mapveya flatMapyerine.
Andres F.

1
@CandiedOrange Ve yine de en çok oy alan cevap (kullanmak için söyleyerek ifPresent) sınamak için başka bir yoldur.
immibis

1
@CandiedOrange ifPresent(x), sadece bir test kadar if(present) {x}. Dönüş değeri önemli değil.
immibis

1
@CandiedOrange Yine de, aslında "test etmek zorunda değiliz" dedin. Aslında "hala test etmek zorunda değiliz" diyemezsiniz, gerçekte, hala test ediyorsunuz.
Aaron

0

Ben en büyük yararı, yöntem imzası bir dönerse olduğunu düşünüyorum EmployeeEğer yok boş olup olmadığını kontrol edin. Bu imza ile bir çalışanın iadesinin garanti edildiğini biliyorsunuz. Bir arıza vakası ele almanıza gerek yok. İsteğe bağlı olarak yaptığınızı biliyorsunuz.

Gördüğüm kodda, insanlar null mümkün olup olmadığını belirlemek için kodu izlemek istemedikleri için hiçbir zaman başarısız olmayacak boş denetimler var, bu yüzden her yerde savunmasız boş denetimler atıyorlar. Bu, kodu biraz yavaşlatır, ancak daha da önemlisi kodu çok gürültülü yapar.

Bununla birlikte, bunun çalışması için, bu kalıbı tutarlı bir şekilde uygulamanız gerekir.


1
Bunu başarmanın en basit yolu , statik analiz kullanmak @Nullableve @ReturnValuesAreNonnullByDefaultbirlikte kullanmaktır .
maaartinus

@maaartinus Dekoratör ek açıklamalarına statik analiz ve dökümantasyon şeklinde ek aletler eklemek daha kolaydır ve zaman API desteği derlenir mi?
Kızak

Gerçekten de ek takım eklemek, kod değişikliği gerektirmediğinden biraz daha basittir. Dahası, yine de diğer böcekleri yakaladığı için sahip olmak istersiniz. +++ Ben ekleyerek önemsiz bir senaryo yazdı package-info.javaile @ReturnValuesAreNonnullByDefaultbu yüzden tüm yapmanız gereken eklemektir, bütün paketlere @NullableGeçmek zorunda nerede Optional. Bu, daha az karakter, daha fazla çöp ve çalışma zamanı ek yükü olmadığı anlamına gelir. Metotun üzerine gelindiğinde belgelere bakmam gerekmiyor. Statik analiz aracı hataları yakalar ve daha sonra null kabiliyet değişiklikleri.
maaartinus

En büyük endişem Optional, null ile başa çıkmanın alternatif bir yoludur. En başından beri Java'daysa, iyi olabilir, ama şimdi sadece bir acı. Kotlin yoluna gitmek IMHO daha iyi olurdu. +++ Not List<@Nullable String>hala dizelerin bir listesidir, ancak List<Optional<String>>değildir.
maaartinus

0

Cevabım: Sadece yapma. En azından gerçekten bir gelişme olup olmadığını iki kere düşünün.

(Başka bir cevaptan alınan) gibi bir ifade

Optional.of(employeeService)                 // definitely have the service
        .map(EmployeeService::getEmployee)   // getEmployee() might return null
        .map(Employee::getId)                // get ID from employee if there is one
        .ifPresent(System.out::println);     // and if there is an ID, print it

başlangıçta nULL olabilecek geri dönüş yöntemleri ile başa çıkmak için doğru fonksiyonel yol gibi görünüyor. Güzel gözüküyor, asla fırlatmıyor ve asla "devamsız" olayı ele almayı düşünmüyor.

Eski tarz yoldan daha iyi görünüyor

Employee employee = employeeService.getEmployee();
if (employee != null) {
    ID id = employee.getId();
    if (id != null) {
        System.out.println(id);
    }
}

Bu da, elsecümlelerin olabileceğini açıkça ortaya koyuyor .

Fonksiyonel tarzı

  • Tamamen farklı görünüyor (ve kullanılmayan insanlar için okunamıyor)
  • hata ayıklamak için bir acı
  • "devamsız" durum için kolay genişletilemez ( orElseher zaman yeterli değildir)
  • "yok" vakasını görmezden gelme yanıltıcı

Hiçbir durumda, derde deva olarak kullanılmamalıdır. Eğer evrensel olarak uygulanabilirse, .operatörün semantiği, ?.operatörün semantiği , NPE'nin unutması ve tüm sorunların göz ardı edilmesi ile değiştirilmelidir.


Büyük bir Java hayranı olurken, işlevsel stilin yalnızca zayıf bir Java adamı ifadesinin yerine geçtiğini söylemeliyim.

employeeService
.getEmployee()
?.getId()
?.apply(id => System.out.println(id));

diğer dillerden iyi biliniyor. Bu ifadeye katılıyor muyuz?

  • (gerçekten) işlevsel değil mi?
  • Eski stil koduna çok benzer, sadece çok daha basit?
  • işlevsel stilden çok daha okunabilir mi?
  • hata ayıklayıcı dostu nedir?

C # 'nin bu konseptin uygulanmasını , Java'nın çekirdek kütüphane sınıfındaki uygulamasından tamamen farklı bir dil özelliği ile tanımlıyor gibi görünüyorsunuz . Bana aynı gözüküyorlar
Caleth,

@Caleth Olmaz. "Güvenli olmayan bir değerlendirmeyi basitleştirme kavramı" hakkında konuşabiliriz, ancak buna "kavram" demezdim. Java'nın aynı şeyi bir çekirdek kütüphane sınıfını kullanarak uyguladığını söyleyebiliriz, ancak bu sadece bir karmaşa. employeeService.getEmployee().getId().apply(id => System.out.println(id));Güvenli gezinme operatörünü bir kez ve bir kez kullanma özelliğini kullanmaya başlamanız yeterlidir Optional. Elbette, buna "uygulama detayı" diyebiliriz, ancak uygulama önemlidir. Lamdaların kodu daha basit ve daha güzel hale getirdiği durumlar var, ama işte bu sadece çirkin bir suistimal.
maaartinus

Kütüphane sınıfı: Optional.of(employeeService).map(EmployeeService::getEmployee).map(Employee::getId).ifPresent(System.out::println);Dil özelliği: employeeService.getEmployee()?.getId()?.apply(id => System.out.println(id));. İki sözdizimi, ama bana çok benziyorlar . Ve "kavram" Optionalaka Nullableaka akaMaybe
Caleth

Neden bunlardan birinin “çirkin suistimal” olduğunu ve diğerinin iyi olduğunu düşündüğünü anlamıyorum. Hem "çirkin suiistimali" hem de her ikisinin de iyi olduğunu düşündüğünü anlayabiliyorum .
Caleth

@Caleth Bir fark, iki soru işareti eklemek yerine, hepsini yeniden yazmak zorunda olmanızdır. Sorun, Dil özelliği ve Kütüphane sınıfı kodlarının çok farklı olmamasıdır. Bu iyi. Sorun orjinal kod ve Library sınıf kodlarının çok farklı olmasıdır. Aynı fikir, farklı kod. Bu çok kötü. +++Kötüye kullanılan şey, çürütülebilirliği ele almak için kullanılan lamdallardır. Lamdalar iyi, ama Optionaldeğil. Null'un sadece Kotlin'de olduğu gibi uygun bir dil desteğine ihtiyacı var. Başka bir sorun: List<Optional<String>>İlişkili olmalarına List<String>ihtiyaç duyacağımız yerle ilgili değil.
maaartinus

0

İsteğe bağlı, işlevsel programlamadan gelen bir kavramdır (daha üst düzey bir tür). İçiçe boş null kontrolü yapmaktan kaçınır ve sizi kıyamet piramidinden kurtarır.

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.