Kami baru saja pindah ke Java 8. Sekarang, saya melihat aplikasi dibanjiri Optional
objek.
Sebelum Java 8 (Gaya 1)
Employee employee = employeeServive.getEmployee();
if(employee!=null){
System.out.println(employee.getId());
}
After Java 8 (Gaya 2)
Optional<Employee> employeeOptional = Optional.ofNullable(employeeService.getEmployee());
if(employeeOptional.isPresent()){
Employee employee = employeeOptional.get();
System.out.println(employee.getId());
}
Saya tidak melihat nilai tambah Optional<Employee> employeeOptional = employeeService.getEmployee();
ketika layanan itu sendiri kembali opsional:
Berasal dari latar belakang Java 6, saya melihat Style 1 lebih jelas dan dengan lebih sedikit baris kode. Apakah ada keuntungan nyata yang saya lewatkan di sini?
Dikonsolidasikan dari pemahaman dari semua jawaban dan penelitian lebih lanjut di blog
employeeOptional.isPresent()
tampaknya sepenuhnya kehilangan titik jenis opsi. Sesuai komentar @ MikePartridge, ini tidak direkomendasikan atau idiomatik. Secara eksplisit memeriksa apakah jenis opsi adalah sesuatu atau tidak sama sekali adalah tidak-tidak. Anda seharusnya hanyamap
atauflatMap
lebih dari mereka.Optional
oleh Brian Goetz , yang Arsitek Bahasa Jawa di Oracle.Jawaban:
Style 2 tidak akan cukup Java 8 untuk melihat manfaat penuh. Anda tidak menginginkannya
if ... use
sama sekali. Lihat contoh Oracle . Mengambil saran mereka, kami mendapatkan:Gaya 3
Atau cuplikan yang lebih panjang
sumber
void ifPresent(Consumer<T>, Runnable)
saya akan berdebat untuk larangan totalisPresent
danget
ifPresentOrElse(Consumer<? super T>, Runnable)
.Jika Anda menggunakan
Optional
sebagai "kompatibilitas" lapisan antara API yang lebih tua yang mungkin masih kembalinull
, mungkin akan membantu untuk membuat (non-kosong) Opsional pada tahap terbaru yang Anda yakin memiliki sesuatu. Misalnya, di mana Anda menulis:Saya akan memilih:
Intinya adalah Anda tahu bahwa ada layanan karyawan yang tidak nol , sehingga Anda dapat membungkusnya
Optional
denganOptional.of()
. Kemudian, ketika Anda memanggilgetEmployee()
itu, Anda mungkin atau mungkin tidak mendapatkan karyawan. Karyawan itu mungkin (atau, mungkin, mungkin tidak) memiliki ID. Kemudian, jika Anda berakhir dengan ID, Anda ingin mencetaknya.Tidak perlu secara eksplisit memeriksa adanya null, kehadiran, dll., Dalam kode ini.
sumber
(...).ifPresent(aMethod)
lebihif((...).isPresent()) { aMethod(...); }
? Tampaknya hanya menjadi sintaks lain untuk melakukan operasi yang hampir sama.Set<Children> children = Optional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet);
Sekarang saya dapat memproseschildren
secara seragam, misalnyachildren.forEach(relative::sendGift);
,. Mereka bahkan bisa dikombinasikan:Optional.of(parent).map(Parent::getChildren).orElseGet(Collections::emptySet).forEach(relative::sendGift);
. Semua pelat kosong dari pemeriksaan nol, dll., ...Di samping tidak ada nilai tambah dalam memiliki
Optional
nilai tunggal. Seperti yang Anda lihat, itu hanya mengganti ceknull
dengan cek keberadaan.Ada besar nilai tambah dalam memiliki
Collection
hal-hal seperti itu. Semua metode streaming yang diperkenalkan untuk menggantikan loop tingkat rendah gaya lama memahamiOptional
hal-hal dan melakukan hal yang benar tentang mereka (tidak memprosesnya atau akhirnya mengembalikan yang tidak disetelOptional
). Jika Anda berurusan dengan n item lebih sering daripada dengan item individual (dan 99% dari semua program realistis melakukannya), maka itu adalah peningkatan besar untuk memiliki dukungan bawaan untuk hal-hal yang ada secara opsional. Dalam kasus ideal Anda tidak perlu memeriksanull
atau hadir.sumber
Employee getEmployee()
vsOptional<Employee> getEmployee()
. Meskipun secara teknis keduanya bisa kembalinull
, salah satunya jauh lebih mungkin menyebabkan masalah..map()
tanpa harus memeriksa nol sepanjang jalan itu bagus. MisalnyaOptional.of(service).map(Service::getEmployee).map(Employee::getId).ifPresent(this::submitIdForReview);
,.null
.Selama Anda menggunakan
Optional
seperti API mewah untukisNotNull()
, maka ya, Anda tidak akan menemukan perbedaan dengan hanya memeriksanull
.Hal-hal yang TIDAK boleh Anda gunakan
Optional
untukMenggunakan
Optional
hanya untuk memeriksa keberadaan suatu nilai adalah Bad Code ™:Hal-hal yang harus Anda gunakan
Optional
untukHindari nilai yang tidak ada, dengan memproduksinya jika perlu saat menggunakan metode API pengembalian-nol:
Atau, jika membuat Karyawan baru terlalu mahal:
Juga, buat semua orang tahu bahwa metode Anda mungkin tidak mengembalikan nilai (jika, karena alasan apa pun, Anda tidak dapat mengembalikan nilai default seperti di atas):
Dan akhirnya, ada semua metode seperti aliran yang telah dijelaskan dalam jawaban lain, meskipun itu tidak benar-benar Anda memutuskan untuk menggunakan
Optional
tetapi memanfaatkan yangOptional
disediakan oleh orang lain.sumber
Optional
sebagai peluang yang terlewatkan. "Ini,Optional
buat benda baru ini yang kubuat sehingga kamu terpaksa memeriksa nilai nol. Oh, dan omong-omong ... aku menambahkanget()
metode untuk itu sehingga kamu tidak benar-benar harus memeriksa apa pun;);)" . (...)Optional
bagus sebagai tanda neon "INI MUNGKIN MENJADI NULL", tetapi keberadaannya yang telanjangget()
melumpuhkannya » . Tapi OP menanyakan alasan penggunaanOptional
, bukan alasan menentangnya atau kekurangan desain.Employee employee = Optional.ofNullable(employeeService.getEmployee());
Dalam cuplikan ketiga Anda, bukankah seharusnya tipenyaOptional<Employee>
?get
adalah jika Anda harus berinteraksi dengan beberapa kode lama. Saya tidak dapat memikirkan banyak alasan valid dalam kode baru yang pernah digunakanget
. Yang mengatakan ketika berinteraksi dengan kode warisan seringkali tidak ada alternatif, tetapi masih walen memiliki titik bahwa keberadaanget
pemula menyebabkan menulis kode yang buruk.Map
satu kali dan saya tidak tahu ada orang yang membuat perubahan kompatibel non-mundur drastis, jadi pastikan Anda bisa dengan sengaja mendesain API yang burukOptional
- tidak yakin apa yang terbukti. SebuahOptional
akan masuk akal untuk beberapatryGet
metode sekalipun.Map
adalah contoh yang dikenal semua orang. Tentu saja mereka tidak dapat membuatMap.get
pengembalian Opsional karena kompatibilitas ke belakang, tetapi Anda dapat dengan mudah membayangkan kelas seperti Peta lain yang tidak memiliki kendala kompatibilitas seperti itu. Katakanclass UserRepository {Optional<User> getUserDetails(int userID) {...}}
. Sekarang Anda ingin mendapatkan detail pengguna yang masuk, dan Anda tahu mereka ada karena mereka berhasil masuk dan sistem Anda tidak pernah menghapus pengguna. Jadi kamu lakukantheUserRepository.getUserDetails(loggedInUserID).get()
.Poin utama bagi saya dalam menggunakan Opsional adalah tetap jelas ketika pengembang perlu memeriksa apakah fungsi mengembalikan nilai atau tidak.
sumber
Kesalahan utama Anda adalah Anda masih berpikir dalam istilah yang lebih prosedural. Ini tidak dimaksudkan sebagai kritik terhadap Anda sebagai pribadi, itu hanya sebuah pengamatan. Berpikir dalam istilah yang lebih fungsional datang dengan waktu dan latihan, dan oleh karena itu metodenya hadir dan terlihat seperti hal-hal yang paling jelas benar untuk memanggil Anda. Kesalahan kecil sekunder Anda adalah membuat Opsional di dalam metode Anda. Opsional dimaksudkan untuk membantu mendokumentasikan bahwa sesuatu mungkin atau tidak dapat mengembalikan nilai. Anda mungkin tidak mendapatkan apa-apa.
Ini telah menyebabkan Anda menulis kode yang terbaca dengan sempurna yang tampaknya masuk akal, tetapi Anda tergoda oleh temptresses kembar yang kejam yang didapat dan hadir.
Tentu saja pertanyaannya dengan cepat menjadi "mengapa ada dan hadir di sana?"
Satu hal yang banyak orang lewatkan adalah isPresent () adalah bahwa itu bukan sesuatu yang dibuat untuk kode baru yang ditulis oleh orang-orang sepenuhnya di papan dengan bagaimana lambda sangat berguna, dan yang menyukai hal fungsional.
Namun demikian, memberi kami beberapa (dua) manfaat (?) Baik, bagus, glamourous:
Yang pertama agak sederhana.
Bayangkan Anda memiliki API yang terlihat seperti ini:
Dan Anda memiliki kelas lama menggunakan ini seperti itu (isi bagian yang kosong):
Contoh dibuat-buat untuk memastikan. Tapi bersabarlah di sini.
Java 8 sekarang telah diluncurkan dan kami berusaha keras untuk naik. Jadi salah satu hal yang kami lakukan adalah kami ingin mengganti antarmuka lama kami dengan sesuatu yang mengembalikan Opsional. Mengapa? Karena seperti orang lain telah dengan anggun menyebutkan: Ini mengeluarkan dugaan apakah sesuatu bisa batal atau tidak. Ini sudah ditunjukkan oleh orang lain. Tapi sekarang kita punya masalah. Bayangkan kita punya (maafkan saya ketika saya menekan alt + F7 pada metode yang tidak bersalah), 46 tempat di mana metode ini disebut dalam kode warisan yang teruji dengan baik yang melakukan pekerjaan yang sangat baik. Sekarang Anda harus memperbarui semua ini.
INI adalah tempat dimana Hadir bersinar.
Karena sekarang: Snickers lastSnickers = snickersCounter.lastConsumedSnickers (); if (null == lastSnickers) {melempar NoSuchSnickersException baru (); } else {consumer.giveDiabetes (lastSnickers); }
menjadi:
Dan ini adalah perubahan sederhana yang dapat Anda berikan kepada junior baru: Dia dapat melakukan sesuatu yang bermanfaat, dan dia akan menjelajahi basis kode pada saat yang sama. menang-menang. Bagaimanapun, sesuatu yang mirip dengan pola ini cukup luas. Dan sekarang Anda tidak perlu menulis ulang kode untuk menggunakan lambdas atau apa pun. (Dalam kasus khusus ini akan sepele, tetapi saya meninggalkan contoh pemikiran di mana itu akan sulit sebagai latihan untuk pembaca.)
Perhatikan bahwa ini berarti cara Anda melakukannya pada dasarnya adalah cara untuk menangani kode lawas tanpa melakukan penulisan ulang yang mahal. Jadi bagaimana dengan kode baru?
Nah, dalam kasus Anda, di mana Anda hanya ingin mencetak sesuatu, Anda cukup melakukan:
snickersCounter.lastConsumedSnickers (). ifPresent (System.out :: println);
Yang cukup sederhana, dan sangat jelas. Titik yang perlahan menggelegak ke permukaan kemudian, adalah bahwa ada kasus penggunaan untuk get () dan isPresent (). Mereka ada di sana untuk memungkinkan Anda memodifikasi kode yang ada secara mekanis untuk menggunakan tipe yang lebih baru tanpa terlalu memikirkannya. Karena itu, apa yang Anda lakukan salah arah dengan cara berikut:
Jika Anda ingin menggunakan Opsional sebagai pemeriksaan keamanan-nol sederhana, yang harus Anda lakukan hanyalah ini:
Tentu saja, versi tampan ini terlihat seperti:
Ngomong-ngomong, meskipun tidak diperlukan, saya merekomendasikan penggunaan baris baru per operasi, sehingga lebih mudah dibaca. Mudah dibaca dan dimengerti ketukan setiap hari dalam seminggu.
Ini tentu saja merupakan contoh yang sangat sederhana di mana mudah untuk memahami semua yang kami coba lakukan. Tidak selalu sesederhana ini di kehidupan nyata. Tetapi perhatikan bagaimana dalam contoh ini, apa yang kami ungkapkan adalah niat kami. Kami ingin MENDAPATKAN karyawan, DAPATKAN ID-nya, dan jika mungkin, cetaklah. Ini adalah kemenangan besar kedua dengan Opsional. Itu memungkinkan kita membuat kode yang lebih jelas. Saya juga berpikir bahwa melakukan hal-hal seperti membuat metode yang melakukan banyak hal sehingga Anda dapat memasukkannya ke peta secara umum adalah ide yang bagus.
sumber
map(x).ifPresent(f)
sama sekali tidak masuk akal yang sama dengan intuisiif(o != null) f(x)
. Theif
Versi sangat jelas. Ini adalah kasus lain dari orang-orang yang menggunakan kereta musik populer, * bukan * kasus kemajuan nyata.Optional
. Saya hanya merujuk pada penggunaan opsional dalam kasus khusus ini, dan lebih-lebih pada keterbacaan, karena banyak orang bertindak seperti format ini sama atau lebih mudah dibaca daripadaif
.isPresent
danget
sebanyak mungkin secara realistis) mereka meningkatkan keamanan . Lebih sulit untuk menulis kode yang salah yang gagal memeriksa tidak adanya nilai jika jenisnyaOptional<Whatever>
bukan menggunakan nullableWhatever
.get
, Anda terpaksa memilih apa yang harus dilakukan ketika nilai yang hilang terjadi: Anda akan menggunakanorElse()
(atauorElseGet()
) untuk memasok default, atauorElseThrow()
melempar yang dipilih pengecualian yang mendokumentasikan sifat masalah , yang lebih baik daripada NPE generik yang tidak berarti sampai Anda menggali jejak stack.Saya tidak melihat manfaat dalam Gaya 2. Anda masih perlu menyadari bahwa Anda memerlukan pemeriksaan nol dan sekarang lebih besar, sehingga kurang dapat dibaca.
Gaya yang lebih baik adalah, jika employeeServive.getEmployee () akan mengembalikan Opsional dan kemudian kode tersebut menjadi:
Dengan cara ini Anda tidak dapat memanggilemployeeServive.getEmployee (). GetId () dan Anda melihat bahwa Anda harus menangani valoue opsional entah bagaimana. Dan jika dalam seluruh metode basis kode Anda tidak pernah mengembalikan nol (atau hampir tidak pernah dengan terkenal ke pengecualian tim Anda untuk aturan ini), itu akan meningkatkan keamanan terhadap NPE.
sumber
isPresent()
. Kami menggunakan opsional sehingga kami tidak perlu menguji.isPresent()
. Ini adalah pendekatan yang salah untuk opsional. Gunakanmap
atauflatMap
sebagai gantinya.ifPresent
) hanyalah cara lain untuk menguji.ifPresent(x)
sama seperti ujianif(present) {x}
. Nilai pengembalian tidak relevan.Saya pikir manfaat terbesar adalah bahwa jika tanda tangan metode Anda mengembalikan
Employee
Anda tidak memeriksa nol. Anda tahu dari tanda tangan itu bahwa dijamin akan mengembalikan seorang karyawan. Anda tidak perlu menangani kasus kegagalan. Dengan opsional, Anda tahu Anda melakukannya.Dalam kode yang saya lihat ada cek nol yang tidak akan pernah gagal karena orang tidak ingin melacak kode untuk menentukan apakah nol itu mungkin, sehingga mereka melempar cek nol ke mana-mana secara defensif. Ini membuat kode sedikit lebih lambat, tetapi yang lebih penting itu membuat kode lebih ribut.
Namun, agar ini berfungsi, Anda perlu menerapkan pola ini secara konsisten.
sumber
@Nullable
dan@ReturnValuesAreNonnullByDefault
bersama dengan analisis statis.package-info.java
dengan@ReturnValuesAreNonnullByDefault
semua paket saya, sehingga semua harus saya lakukan adalah untuk menambahkan@Nullable
di mana Anda harus beralih keOptional
. Ini berarti lebih sedikit karakter, tidak ada sampah yang ditambahkan, dan tidak ada runtime-overhead. Saya tidak perlu melihat dokumentasi seperti yang ditunjukkan ketika melayang di atas metode. Alat analisis statis menangkap kesalahan dan kemudian perubahan nullability.Optional
adalah menambahkan cara alternatif untuk menangani nullability. Jika itu di Jawa sejak awal, mungkin baik-baik saja, tapi sekarang itu hanya sakit. Pergi jalan Kotlin akan menjadi IMHO jauh lebih baik. +++ Catatan yangList<@Nullable String>
masih daftar string, sementaraList<Optional<String>>
tidak.Jawaban saya adalah: Jangan. Setidaknya pikirkan dua kali tentang apakah itu benar-benar perbaikan.
Ekspresi (diambil dari jawaban lain) seperti
tampaknya menjadi cara fungsional yang tepat untuk menangani metode pengembalian yang semula nullable. Kelihatannya bagus, tidak pernah melempar dan tidak pernah membuat Anda berpikir untuk menangani kasus "absen".
Itu terlihat lebih baik daripada cara gaya lama
yang membuatnya jelas bahwa mungkin ada
else
klausa.Gaya fungsional
orElse
tidak selalu memadai)Dalam hal apapun itu tidak harus digunakan sebagai obat mujarab. Jika itu berlaku secara universal, maka semantik
.
operator harus diganti dengan semantik?.
operator , NPE dilupakan dan semua masalah diabaikan.Sementara menjadi penggemar Java yang besar, saya harus mengatakan bahwa gaya fungsional hanyalah pengganti pernyataan orang Jawa yang buruk
terkenal dari bahasa lain. Apakah kita setuju dengan pernyataan ini?
sumber
employeeService.getEmployee().getId().apply(id => System.out.println(id));
dan buat null-safe sekali menggunakan operator navigasi Aman dan sekali menggunakanOptional
. Tentu, kita bisa menyebutnya "detail implementasi", tetapi implementasi penting. Ada kasus-kasus ketika para lama membuat kode lebih sederhana dan lebih baik, tetapi di sini hanya sebuah penyalahgunaan yang buruk.Optional.of(employeeService).map(EmployeeService::getEmployee).map(Employee::getId).ifPresent(System.out::println);
Fitur Bahasa:employeeService.getEmployee()?.getId()?.apply(id => System.out.println(id));
. Dua sintaks, tetapi akhirnya tampak sangat mirip dengan saya. Dan "konsep" adalahOptional
aliasNullable
aliasMaybe
+++
Apa yang disalahgunakan adalah lamdas untuk menangani nullability. Lamdas baik-baik saja, tetapiOptional
tidak. Nullability hanya membutuhkan dukungan bahasa yang tepat seperti di Kotlin. Masalah lain:List<Optional<String>>
tidak berhubungan denganList<String>
, di mana kita membutuhkan mereka untuk berhubungan.Opsional adalah konsep (tipe urutan lebih tinggi) yang berasal dari pemrograman fungsional. Menggunakannya tidak jauh dengan pemeriksaan null bersarang dan menyelamatkan Anda dari piramida malapetaka.
sumber