Mengapa menggunakan preferensi preferensial untuk memeriksa variabel?

25

Ambil dua contoh kode:

if(optional.isPresent()) {
    //do your thing
}

if(variable != null) {
    //do your thing
}

Sejauh yang saya tahu perbedaan yang paling jelas adalah bahwa Opsional membutuhkan pembuatan objek tambahan.

Namun, banyak orang sudah mulai mengadopsi Opsional dengan cepat. Apa keuntungan menggunakan opsional dibandingkan cek nol?

William Dunne
sumber
4
Anda hampir tidak pernah ingin menggunakan isPresent, Anda biasanya harus menggunakan ifPresent atau map
jk.
6
@jk. Karena ifpernyataannya sangaaaat dekade terakhir, dan semua orang menggunakan abstraksi monad dan lambda sekarang.
user253751
2
@ imibis Saya pernah berdebat panjang tentang topik ini dengan pengguna lain. Intinya adalah bahwa if(x.isPresent) fails_on_null(x.get)Anda keluar dari sistem tipe dan harus menjaga jaminan bahwa kode tidak akan memecahkan "di kepala Anda" selama jarak (diakui pendek) antara kondisi dan pemanggilan fungsi. Dalam optional.ifPresent(fails_on_null)tipe sistem membuat jaminan ini untuk Anda, dan Anda tidak perlu khawatir.
ziggystar
1
Kelemahan utama dalam Java dengan menggunakan Optional.ifPresent(dan berbagai konstruksi Java lainnya) adalah Anda hanya dapat memodifikasi variabel final secara efektif dan tidak dapat membuang pengecualian yang diperiksa. Itulah alasan yang cukup sering dihindari ifPresent, sayangnya.
Mike

Jawaban:

19

Optionalmemanfaatkan tipe sistem untuk melakukan pekerjaan yang seharusnya Anda lakukan semua di kepala Anda: mengingat apakah referensi yang diberikan mungkin atau tidak null. Ini bagus. Itu selalu pintar untuk membiarkan kompiler menangani obat bius yang membosankan, dan menyimpan pemikiran manusia untuk pekerjaan yang kreatif dan menarik.

Tanpa Optional, setiap referensi dalam kode Anda seperti bom yang tidak meledak. Mengaksesnya dapat melakukan sesuatu yang bermanfaat, atau dapat menghentikan program Anda dengan pengecualian.

Dengan Opsional dan tanpa null, setiap akses ke referensi normal berhasil, dan setiap referensi ke Opsional berhasil kecuali itu tidak disetel dan Anda gagal memeriksa untuk itu. Itu adalah kemenangan besar dalam pemeliharaan.

Sayangnya, sebagian besar bahasa yang sekarang menawarkan Optionalbelum dihapuskan null, jadi Anda hanya dapat mengambil untung dari konsep tersebut dengan melembagakan kebijakan ketat "sama sekali tidak null, pernah". Oleh karena itu, Optionalmisalnya Java tidak semenarik yang seharusnya.

Kilian Foth
sumber
Karena jawaban Anda adalah yang teratas, mungkin ada baiknya menambahkan penggunaan Lambdas sebagai keuntungan - bagi siapa pun yang membaca ini di masa depan (lihat jawaban saya)
William Dunne
Kebanyakan bahasa modern menyediakan jenis yang tidak dapat dibatalkan, Kotlin, naskah, AFAIK.
Zen
5
IMO ini lebih baik ditangani dengan penjelasan @Nullable. Tidak ada alasan untuk menggunakan Optionalhanya untuk mengingatkan Anda bahwa nilainya bisa nol
Hilikus
18

Sebuah Optionalmemasukkan pengetikan yang lebih kuat ke dalam operasi yang mungkin gagal, seperti yang dijawab oleh jawaban lain, tetapi itu jauh dari hal yang paling menarik atau berharga yang dibahas Optionals. Jauh lebih berguna adalah kemampuan untuk menunda atau menghindari memeriksa kegagalan, dan dengan mudah menyusun banyak operasi yang mungkin gagal.

Pertimbangkan jika Anda memiliki optionalvariabel dari kode contoh Anda, maka Anda harus melakukan dua langkah tambahan yang masing-masing berpotensi gagal. Jika ada langkah yang gagal, Anda ingin mengembalikan nilai default. Menggunakan Optionalsdengan benar, Anda berakhir dengan sesuatu seperti ini:

return optional.flatMap(x -> x.anotherOptionalStep())
               .flatMap(x -> x.yetAnotherOptionalStep())
               .orElse(defaultValue);

Dengan nullsaya harus memeriksa tiga kali nullsebelum melanjutkan, yang menambahkan banyak kerumitan dan sakit kepala pemeliharaan kode. Optionalsmemiliki pemeriksaan bawaan ke flatMapdan orElsefungsi.

Catatan saya tidak menelepon isPresentsekali, yang harus Anda anggap sebagai bau kode saat menggunakan Optionals. Itu tidak berarti Anda tidak boleh menggunakan isPresent, hanya saja Anda harus memeriksa dengan cermat setiap kode yang ada, untuk melihat apakah ada cara yang lebih baik. Jika tidak, Anda benar, Anda hanya mendapatkan manfaat keamanan tipe marginal daripada menggunakan null.

Perhatikan juga bahwa saya tidak khawatir tentang merangkum semua ini menjadi satu fungsi, untuk melindungi bagian lain dari kode saya dari null pointer dari hasil antara. Jika lebih masuk akal untuk memiliki saya .orElse(defaultValue)di fungsi lain misalnya, saya memiliki keraguan jauh lebih sedikit tentang meletakkannya di sana, dan itu jauh lebih mudah untuk menyusun operasi antara fungsi yang berbeda sesuai kebutuhan.

Karl Bielefeldt
sumber
10

Ini menyoroti kemungkinan nol sebagai respons yang valid, yang sering diasumsikan orang (benar atau salah) tidak akan dikembalikan. Menyoroti beberapa kali ketika null valid memungkinkan menghilangkan membuang sampah sembarangan kode Anda dengan sejumlah besar cek nol sia-sia.

Idealnya mengatur variabel ke nol akan menjadi kesalahan waktu kompilasi di mana saja tetapi dalam opsional; menghilangkan pengecualian null pointer runtime. Sayangnya kompatibilitas mundur menghalangi ini, sayangnya

Richard Tingle
sumber
4

Biarkan saya memberi Anda sebuah contoh:

class UserRepository {
     User findById(long id);
}

Cukup jelas untuk apa metode ini dimaksudkan. Tetapi apa yang terjadi jika repositori tidak mengandung pengguna dengan id yang diberikan? Bisa melempar pengecualian atau mungkin mengembalikan nol?

Contoh kedua:

class UserRepository {
     Optional<User> findById(long id);
}

Sekarang, apa yang terjadi di sini jika tidak ada pengguna dengan id yang diberikan? Benar, Anda mendapatkan instance Optional.absent () dan Anda dapat memeriksanya dengan Optional.isPresent (). Metode tanda tangan dengan Opsional lebih eksplisit tentang kontraknya. Segera jelas, bagaimana metode ini seharusnya berperilaku.

Ini juga memiliki manfaat ketika membaca kode klien:

User user = userRepository.findById(userId).get();

Saya telah melatih mata saya untuk melihat .get () sebagai kode bau langsung. Ini berarti bahwa klien sengaja mengabaikan kontrak dari metode yang dipanggil. Ini jauh lebih mudah untuk melihat ini daripada tanpa Opsional, karena dalam hal ini Anda tidak segera menyadari apa yang disebut metode kontrak (apakah itu memungkinkan nol atau tidak dalam pengembaliannya) sehingga Anda perlu memeriksanya terlebih dahulu.

Opsional digunakan dengan konvensi bahwa metode dengan tipe pengembalian Opsional tidak pernah mengembalikan nol dan biasanya juga dengan konvensi (kurang kuat) bahwa metode tanpa jenis pengembalian opsional tidak pernah mengembalikan nol (tetapi lebih baik untuk membubuhi keterangan dengan @Nonnull, @NotNull atau yang serupa) .

qbd
sumber
3

Sebuah Optional<T>memungkinkan Anda untuk memiliki "kegagalan" atau "tidak ada hasil" sebagai nilai respons / pengembalian yang valid untuk metode Anda (pikirkan, misalnya, pencarian basis data). Menggunakan Optionaldaripada menggunakan nulluntuk menunjukkan kegagalan / tidak ada hasil memiliki beberapa keuntungan:

  • Jelas dikomunikasikan bahwa "kegagalan" adalah sebuah pilihan. Pengguna metode Anda tidak perlu menebak apakah nullmungkin dikembalikan.
  • Nilai nullseharusnya, setidaknya menurut pendapat saya, tidak digunakan untuk menunjukkan apa pun karena semantiknya mungkin tidak sepenuhnya jelas. Ini harus digunakan untuk memberi tahu pemulung bahwa objek yang sebelumnya dirujuk dapat dikumpulkan.
  • The Optionalkelas menyediakan banyak metode yang bagus untuk kondisional bekerja dengan nilai-nilai (misalnya, ifPresent()) atau menetapkan nilai default dengan cara transparan ( orElse()).

Sayangnya, itu tidak sepenuhnya menghapus perlunya nullpemeriksaan dalam kode karena Optionalobjek itu sendiri masih mungkin null.

pansen
sumber
4
Ini bukan untuk apa Opsional. Untuk melaporkan kesalahan, gunakan pengecualian. Jika Anda tidak bisa melakukan itu, gunakan jenis yang berisi baik hasil atau kode kesalahan .
James Youngman
Saya sangat tidak setuju. Optional.empty () adalah cara yang bagus untuk menunjukkan kegagalan atau tidak adanya menurut saya.
LegendLength
@LegendLength, saya harus sangat tidak setuju jika terjadi kegagalan. Jika suatu fungsi gagal, lebih baik beri saya penjelasan, bukan hanya yang kosong, "Saya gagal"
Winston Ewert
Maaf saya setuju, maksud saya 'kegagalan yang diharapkan' karena tidak menemukan karyawan dengan nama 'x' selama pencarian.
LegendLength
3

Selain jawaban lain, keuntungan utama lainnya dari Opsional ketika menulis kode bersih adalah Anda bisa menggunakan ekspresi Lambda jika nilainya ada, seperti yang ditunjukkan di bawah ini:

opTr.ifPresent(tr -> transactions.put(tr.getTxid(), tr));
William Dunne
sumber
2

Pertimbangkan metode berikut:

void dance(Person partner)

Sekarang mari kita lihat beberapa kode panggilan:

dance(null);

Hal ini menyebabkan berpotensi sulit untuk melacak kerusakan di tempat lain, karena dancetidak mengharapkan nol.

dance(optionalPerson)

Ini menyebabkan kesalahan kompilasi, karena tarian mengharapkan PersonbukanOptional<Person>

dance(optionalPerson.get())

Jika saya tidak memiliki seseorang, ini menyebabkan pengecualian pada baris ini, pada titik di mana saya secara tidak sah mencoba mengubah orang tersebut Optional<Person>menjadi Seseorang. Kuncinya adalah bahwa tidak seperti melewatkan nol, kecuali jejak ke sini, bukan beberapa lokasi lain dalam program.

Untuk menyatukan semuanya: penyalahgunaan opsional menghasilkan lebih mudah untuk melacak masalah, penyalahgunaan nol menyebabkan sulit untuk melacak masalah.

Winston Ewert
sumber
1
Jika Anda melempar pengecualian ketika memanggil Optional.get()kode Anda ditulis dengan buruk - Anda seharusnya hampir tidak pernah menelepon Optional.get tanpa memeriksa Optional.isPresent()terlebih dahulu (seperti yang ditunjukkan dalam OP) atau Anda dapat menulis optionalPersion.ifPresent(myObj::dance).
Chris Cooper
@QmunkE, ya, Anda hanya perlu memanggil Optional.get () jika Anda sudah tahu bahwa opsional tidak kosong (baik karena Anda memanggil isPresent atau karena algoritma Anda menjaminnya). Namun, saya khawatir tentang orang-orang yang secara buta memeriksa isPresent dan kemudian mengabaikan nilai-nilai yang tidak ada menciptakan sejumlah kegagalan diam yang akan menjadi rasa sakit untuk dilacak.
Winston Ewert
1
Saya suka opsional. Tapi saya akan mengatakan bahwa null pointer biasa sangat jarang menjadi masalah dalam kode dunia nyata. Mereka hampir selalu gagal di dekat garis kode yang menyinggung. Dan saya pikir hal itu menjadi berlebihan oleh orang-orang ketika membicarakan hal ini.
LegendLength
@ LegegLength, pengalaman saya adalah bahwa 95% kasus memang mudah dilacak untuk mengidentifikasi dari mana NULL berasal. 5% sisanya, bagaimanapun, sangat sulit untuk dilacak.
Winston Ewert
-1

Di Objective-C, semua referensi objek adalah opsional. Karena itu, kode seperti yang Anda poskan konyol.

if (variable != nil) {
    [variable doThing];
}

Kode seperti di atas tidak pernah terdengar dalam Objective-C. Sebaliknya, programmer hanya akan:

[variable doThing];

Jika variableberisi nilai, maka doThingakan dipanggil di sana. Jika tidak, tidak ada salahnya. Program akan memperlakukannya sebagai larangan dan terus berjalan.

Ini adalah manfaat nyata dari opsional. Anda tidak perlu membuang kode Anda if (obj != nil).

Daniel T.
sumber
Bukankah itu hanya bentuk kegagalan yang diam-diam? Dalam beberapa kasus, Anda mungkin peduli bahwa nilainya nol, dan ingin melakukan sesuatu tentangnya.
LegendLength
-3

if (opsional.isPresent ()) {

Masalah yang jelas di sini adalah bahwa jika "opsional" benar - benar hilang (yaitu nol) maka kode Anda akan meledak dengan NullReferenceException (atau serupa) yang mencoba memanggil metode apa pun pada referensi objek nol!

Anda mungkin ingin menulis metode Helper-class statis yang menentukan null-ness dari setiap jenis objek yang diberikan, seperti ini:

function isNull( object o ) 
{
   return ( null == o ); 
}

if ( isNull( optional ) ) ... 

atau, mungkin, lebih bermanfaat:

function isNotNull( object o ) 
{
   return ( null != o ); 
}

if ( isNotNull( optional ) ) ... 

Tetapi apakah salah satu dari ini benar-benar lebih dapat dibaca / dimengerti / dipelihara daripada yang asli?

if ( null == optional ) ... 
if ( null != optional ) ... 
Phill W.
sumber