Setelah menggunakan Java 8 sekarang selama 6+ bulan atau lebih, saya cukup senang dengan perubahan API baru. Satu area yang saya masih belum percaya adalah kapan harus digunakan Optional
. Saya tampaknya berayun di antara keinginan untuk menggunakannya di mana saja sesuatu mungkin terjadi null
, dan tidak ada tempat sama sekali.
Tampaknya ada banyak situasi di mana saya bisa menggunakannya, dan saya tidak pernah yakin apakah itu menambah manfaat (keterbacaan / keamanan nol) atau hanya menyebabkan overhead tambahan.
Jadi, saya punya beberapa contoh, dan saya akan tertarik pada pemikiran masyarakat tentang apakah Optional
itu bermanfaat.
1 - Sebagai tipe pengembalian metode publik saat metode tersebut dapat kembali null
:
public Optional<Foo> findFoo(String id);
2 - Sebagai parameter metode saat param mungkin null
:
public Foo doSomething(String id, Optional<Bar> barOptional);
3 - Sebagai anggota opsional kacang:
public class Book {
private List<Pages> pages;
private Optional<Index> index;
}
4 - Dalam Collections
:
Secara umum saya tidak berpikir:
List<Optional<Foo>>
menambahkan sesuatu - terutama karena seseorang dapat menggunakan filter()
untuk menghapus null
nilai dll, tetapi apakah ada kegunaan yang baik untuk Optional
koleksi?
Adakah kasus yang saya lewatkan?
Map<Character, String>
. Jika tidak ada substitusi saya dapat menggunakan ini:Optional.ofNullable(map.get(c)).orElse(String.valueOf(c))
. Perhatikan juga bahwa Opsional dicuri dari Guava dan memiliki sintaks yang jauh lebih baik:Optional.fromNullable(map.get(c)).or(String.valueOf(c));
.filter(Optional::absent)
"getOrDefault()
?Jawaban:
Poin utama
Optional
adalah untuk menyediakan sarana untuk fungsi mengembalikan nilai untuk menunjukkan tidak adanya nilai kembali. Lihat diskusi ini . Ini memungkinkan pemanggil untuk melanjutkan serangkaian panggilan metode lancar.Ini paling cocok dengan kasus penggunaan # 1 dalam pertanyaan OP. Meskipun, tidak adanya nilai adalah formulasi yang lebih tepat daripada nol karena sesuatu seperti
IntStream.findFirst
tidak pernah bisa mengembalikan nol.Untuk use case # 2 , meneruskan argumen opsional ke suatu metode, ini bisa dibuat berfungsi, tapi agak canggung. Misalkan Anda memiliki metode yang mengambil string diikuti oleh string kedua opsional. Menerima
Optional
sebagai argumen kedua akan menghasilkan kode seperti ini:Bahkan menerima null lebih baik:
Mungkin yang terbaik adalah memiliki metode kelebihan beban yang menerima argumen string tunggal dan menyediakan default untuk yang kedua:
Ini memang memiliki keterbatasan, tetapi jauh lebih bagus daripada yang di atas.
Gunakan case # 3 dan # 4 , memiliki
Optional
dalam bidang kelas atau dalam struktur data, dianggap sebagai penyalahgunaan API. Pertama, itu bertentangan dengan tujuan desain utamaOptional
seperti yang dinyatakan di atas. Kedua, itu tidak menambah nilai apa pun.Ada tiga cara untuk menangani ketiadaan nilai dalam
Optional
: untuk memberikan nilai pengganti, memanggil fungsi untuk memberikan nilai pengganti, atau untuk melemparkan pengecualian. Jika Anda menyimpan ke dalam bidang, Anda akan melakukan ini pada inisialisasi atau waktu penugasan. Jika Anda menambahkan nilai ke dalam daftar, seperti yang disebutkan OP, Anda memiliki pilihan tambahan yaitu tidak menambahkan nilai, sehingga "mendatarkan" nilai-nilai yang tidak ada.Saya yakin seseorang dapat membuat beberapa kasus yang dibuat-buat di mana mereka benar-benar ingin menyimpan
Optional
dalam bidang atau koleksi, tetapi secara umum, yang terbaik adalah menghindari melakukan ini.sumber
Optional
sebagai bidang ketika hal-hal harus dilakukan atau tidak dilakukan pada ada atau tidak adanya nilai.if(foo == null)
internal akan lebih baik daripada hanya menyimpan bidang sebagaioptional
. Dan saya tidak melihat mengapa secara eksplisit memanggilgetOptionalFoo()
internal lebih baik daripada hanya menyimpan bidang sebagaiOptional
. Juga, jika satu bidang bisanull
dan satu bidang tidak bisanull
, mengapa tidak mengomunikasikannya ke kompiler dengan membuatnyaOptional
atau tidakOptional
.Optional<>
pakar: "jangan menyimpanOptional<>
di lapangan". Saya benar-benar berusaha memahami inti dari rekomendasi ini. Pertimbangkan pohon DOM di mana simpul dapat memiliki orang tua atau tidak. Tampaknya dalam use case yangNode.getParent()
akan kembaliOptional<Node>
. Apakah saya benar-benar diharapkan untuk menyimpannull
dan membungkus hasilnya setiap kali? Mengapa? Apa yang saya beli dari inefisiensi ekstra ini, selain membuat kode saya terlihat jelek?Optional<Node> getParent() { return Optional.ofNullable(parent); }
. Ini terlihat mahal karena mengalokasikanOptional
setiap waktu! Tetapi jika penelepon membukanya segera, objek tersebut berumur pendek dan tidak pernah dipromosikan keluar dari ruang eden. Bahkan mungkin bisa dihilangkan dengan analisis pelarian JIT. Jawaban intinya adalah "itu tergantung" seperti biasa, tetapi menggunakanOptional
di bidang berpotensi menggembungkan penggunaan memori dan memperlambat traversal struktur data. Dan akhirnya saya pikir itu mengacaukan kode, tapi ini masalah selera.Saya terlambat ke permainan tetapi untuk apa nilainya, saya ingin menambahkan 2 Cents saya. Mereka menentang tujuan desain
Optional
, yang dirangkum dengan baik oleh jawaban Stuart Marks , tapi saya masih yakin akan validitasnya (jelas).Gunakan Opsional Di Mana Saja
Secara umum
Saya menulis seluruh posting blog tentang menggunakan
Optional
tetapi pada dasarnya turun ke ini:Optional
alih-alihnull
Dua pengecualian pertama dapat mengurangi persepsi overhead tentang membungkus dan membuka bungkus referensi
Optional
. Mereka dipilih sedemikian rupa sehingga nol tidak pernah bisa secara legal melewati batas dari satu instance ke instance lainnya.Perhatikan bahwa ini hampir tidak akan pernah memungkinkan
Optional
dalam koleksi yang hampir seburuknull
s. Tapi jangan lakukan itu. ;)Mengenai pertanyaan Anda
Keuntungan
Melakukan hal ini mengurangi keberadaan
null
s di basis kode Anda, meskipun tidak membasmi mereka. Tapi itu bahkan bukan poin utama. Ada keuntungan penting lainnya:Maksud Klarifikasi
Menggunakan
Optional
dengan jelas menyatakan bahwa variabelnya, yah, opsional. Setiap pembaca kode atau konsumen API Anda akan dikalahkan secara langsung dengan fakta bahwa mungkin tidak ada apa-apa di sana dan bahwa pemeriksaan diperlukan sebelum mengakses nilainya.Menghapus Ketidakpastian
Tanpa
Optional
makna suatunull
kejadian tidak jelas. Ini bisa berupa representasi hukum suatu negara (lihatMap.get
) atau kesalahan implementasi seperti inisialisasi yang hilang atau gagal.Ini berubah secara dramatis dengan penggunaan
Optional
. Di sini, sudah terjadinya darinull
menandakan keberadaan bug. (Karena jika nilainya dibiarkan hilang, makaOptional
akan digunakan.) Ini membuat debugging pengecualian null pointer jauh lebih mudah karena pertanyaan tentang makna ininull
sudah dijawab.Lebih Banyak Cek kosong
Sekarang tidak ada yang bisa
null
lagi, ini bisa diberlakukan di mana-mana. Baik dengan anotasi, pernyataan atau cek biasa, Anda tidak perlu memikirkan apakah argumen ini atau tipe pengembalian itu bisa nol. Tidak bisa!Kekurangan
Tentu saja, tidak ada peluru perak ...
Performa
Membungkus nilai-nilai (terutama primitif) menjadi contoh tambahan dapat menurunkan kinerja. Dalam loop ketat ini mungkin menjadi nyata atau bahkan lebih buruk.
Perhatikan bahwa kompiler mungkin dapat mengelak dari referensi tambahan untuk umur pendek
Optional
s. Di Jawa 10 jenis nilai lebih lanjut dapat mengurangi atau menghapus penalti.Serialisasi
Optional
tidak serializable tetapi solusinya tidak terlalu rumit.Invarian
Karena invarian tipe generik di Jawa, operasi tertentu menjadi rumit ketika tipe nilai aktual didorong ke argumen tipe generik. Contoh diberikan di sini (lihat "Polimorfisme parametrik") .
sumber
List
s. Ini adalah kasus ketika penting bahwa elemen tertentu terletak pada indeks tertentu, tetapi elemen tersebut dapat hadir atau tidak. Itu jelas dikomunikasikan olehList<Optional<T>>
tipe. Jika di sisi lain hanya penting objek yang merupakan anggota beberapa koleksi maka tidak ada gunanya.Optional
yang diteruskan ke suatu metode bisanull
. Jadi, apa yang telah Anda dapatkan? Sekarang Anda memiliki dua cek untuk dilakukan. Lihat stackoverflow.com/a/31923042/650176Optional
(yang seharusnya menjadi masalah jika Anda bersedia memberikannya sebagai argumen)null
tidak pernah merupakan nilai hukum. Jadi memanggil metode dengan parameter eksplisitnull
jelas merupakan kesalahan.Secara pribadi, saya lebih suka menggunakan IntelliJ's Code Inspection Tool untuk menggunakan
@NotNull
dan@Nullable
memeriksa karena ini sebagian besar waktu kompilasi (dapat memiliki beberapa pemeriksaan runtime) Ini memiliki overhead yang lebih rendah dalam hal pembacaan kode dan kinerja runtime. Ini tidak seketat menggunakan Opsional, namun kurangnya kekakuan ini harus didukung oleh tes unit yang layak.Ini berfungsi dengan Java 5 dan tidak perlu untuk membungkus dan membuka nilai. (atau buat objek pembungkus)
sumber
@Nonnull
secara pribadi - bekerja dengan FindBugs;)@Nonnull @NotNull etc
Saya pikir Pilihan Guava dan halaman wiki mereka menempatkannya dengan cukup baik:
Optional
menambahkan beberapa overhead, tapi saya pikir keuntungan jelasnya adalah untuk membuatnya eksplisit bahwa suatu objek mungkin tidak ada dan itu memaksa bahwa programmer menangani situasi. Ini mencegah seseorang melupakan!= null
cek yang dicintai .Mengambil contoh 2 , saya pikir ini adalah kode yang jauh lebih eksplisit untuk ditulis:
dari
Bagi saya, semakin
Optional
baik menangkap fakta bahwa tidak ada kartu suara.2 ¢ saya tentang poin Anda:
public Optional<Foo> findFoo(String id);
- Saya tidak yakin tentang ini. Mungkin saya akan mengembalikanResult<Foo>
yang mungkin kosong atau mengandungFoo
. Ini adalah konsep yang mirip, tetapi tidak benar-benar sebuahOptional
.public Foo doSomething(String id, Optional<Bar> barOptional);
- Saya lebih suka @Nullable dan cek findbugs, seperti dalam jawaban Peter Lawrey - lihat juga diskusi ini .Optional<Index> getIndex()
untuk secara eksplisit menunjukkan bahwa buku itu mungkin tidak memiliki indeks.Secara umum, saya akan mencoba untuk meminimalkan melewati
null
. (Sekali terbakar ...) Saya pikir itu layak untuk menemukan abstraksi yang sesuai dan menunjukkan kepada sesama programmer apa nilai pengembalian tertentu sebenarnya mewakili.sumber
soundcard.ifPresent(System.out::println)
. PanggilanisPresent
secara semantik sama dengan memeriksanull
.if(soundcard != null && soundcard.isPresent())
. Meskipun membuat api yang mengembalikan Opsional dan juga bisa mengembalikan nol adalah sesuatu yang saya harap tidak ada yang pernah melakukannya.Dari tutorial Oracle :
sumber
Berikut ini adalah artikel bagus yang menunjukkan kegunaan usecase # 1. Ada kode ini
ditransformasikan ke ini
dengan menggunakan Opsional sebagai nilai balik metode pengambil masing-masing .
sumber
Berikut ini adalah penggunaan yang menarik (saya percaya) untuk ... Tes.
Saya berniat menguji salah satu proyek saya dan karena itu saya membangun pernyataan; hanya ada hal-hal yang harus saya verifikasi dan yang lain tidak.
Karena itu saya membangun hal-hal untuk menegaskan dan menggunakan pernyataan untuk memverifikasi mereka, seperti ini:
Di kelas NodeAssert, saya melakukan ini:
Yang berarti pernyataan benar-benar hanya memicu jika saya ingin memeriksa labelnya!
sumber
Optional
kelas memungkinkan Anda menghindari menggunakannull
dan memberikan alternatif yang lebih baik:Ini mendorong pengembang untuk melakukan pemeriksaan kehadiran agar tidak tertangkap
NullPointerException
.API menjadi lebih baik didokumentasikan karena dimungkinkan untuk dilihat, di mana mengharapkan nilai yang tidak ada.
Optional
menyediakan API yang nyaman untuk pekerjaan lebih lanjut dengan objekisPresent()
:;get()
;orElse()
;orElseGet()
;orElseThrow()
;map()
;filter()
;flatmap()
.Selain itu, banyak kerangka kerja yang secara aktif menggunakan tipe data ini dan mengembalikannya dari API mereka.
sumber
Di java, jangan gunakan itu kecuali Anda kecanduan pemrograman fungsional.
Mereka tidak memiliki tempat sebagai argumen metode (saya menyarankan seseorang suatu hari akan memberikan Anda opsional nol, bukan hanya pilihan yang kosong).
Mereka masuk akal untuk nilai-nilai pengembalian tetapi mereka mengundang kelas klien untuk terus memperluas rantai pengembangan perilaku.
FP dan rantai memiliki sedikit tempat dalam bahasa imperatif seperti java karena membuatnya sangat sulit untuk di-debug, bukan hanya untuk membaca. Ketika Anda melangkah ke garis, Anda tidak bisa mengetahui keadaan atau niat program; Anda harus melangkah untuk mengetahuinya (ke dalam kode yang sering kali bukan milik Anda dan banyak bingkai tumpukan meskipun filter langkah) dan Anda harus menambahkan banyak breakpoints untuk memastikan itu bisa berhenti dalam kode / lambda yang Anda tambahkan, alih-alih hanya berjalan di jalur if / else / call trivial.
Jika Anda ingin pemrograman fungsional, pilih sesuatu yang lain daripada java dan harap Anda memiliki alat untuk debug itu.
sumber
Saya tidak berpikir bahwa Opsional adalah pengganti umum untuk metode yang berpotensi mengembalikan nilai nol.
Gagasan dasarnya adalah: Tidak adanya nilai tidak berarti bahwa nilai tersebut berpotensi tersedia di masa mendatang. Ini perbedaan antara findById (-1) dan findById (67).
Informasi utama Opsional untuk penelepon adalah bahwa ia mungkin tidak mengandalkan nilai yang diberikan tetapi mungkin tersedia pada suatu waktu. Mungkin itu akan menghilang lagi dan kembali lagi nanti. Ini seperti saklar on / off. Anda memiliki "opsi" untuk menghidupkan atau mematikan lampu. Tetapi Anda tidak memiliki pilihan jika Anda tidak memiliki lampu untuk menyala.
Jadi saya merasa terlalu berantakan untuk memperkenalkan Opsional di mana-mana di mana sebelumnya null berpotensi dikembalikan. Saya masih akan menggunakan null, tetapi hanya di area terbatas seperti akar pohon, inisialisasi malas dan metode pencarian eksplisit.
sumber
Tampaknya
Optional
hanya berguna jika jenis T di Opsional adalah tipe primitif sepertiint
,long
,char
, dll Untuk "nyata" kelas, itu tidak masuk akal bagi saya karena Anda dapat menggunakannull
nilai pula.Saya pikir itu diambil dari sini (atau dari konsep bahasa lain yang serupa).
Nullable<T>
Di C # ini
Nullable<T>
sudah lama diperkenalkan untuk membungkus tipe nilai.sumber
Optional
sebenarnyajava.util.Optional
.Sebuah memiliki semantik yang mirip dengan sebuah contoh unmodifiable dari pola desain Iterator :
Optional
isPresent()
)get()
) jika itu merujuk ke suatu objeknext()
metode).Karena itu pertimbangkan untuk mengembalikan atau melewati
Optional
konteks di mana Anda sebelumnya mungkin telah mempertimbangkan menggunakan JavaIterator
.sumber
Java SE 8 memperkenalkan kelas baru yang disebut java.util.Optional,
Anda dapat membuat Opsional kosong atau Opsional dengan nilai nol.
Dan di sini adalah Opsional dengan nilai bukan nol:
Lakukan Sesuatu Jika Suatu Nilai Hadir
Sekarang Anda memiliki objek opsional, Anda dapat mengakses metode yang tersedia untuk secara eksplisit menangani ada atau tidaknya nilai. Daripada harus ingat untuk melakukan cek nol, sebagai berikut:
Anda dapat menggunakan metode ifPresent (), sebagai berikut:
sumber