Apakah menggunakan ekspresi Lambda bila memungkinkan dalam java praktik yang baik?

52

Saya baru-baru ini menguasai ekspresi Lambda yang diperkenalkan di java 8. Saya menemukan bahwa setiap kali saya menggunakan antarmuka fungsional, saya cenderung selalu menggunakan ekspresi Lambda alih-alih membuat kelas yang mengimplementasikan antarmuka fungsional.

Apakah ini dianggap praktik yang baik? Atau situasi mereka di mana menggunakan Lambda untuk antarmuka fungsional tidak tepat?

SteelToe
sumber
122
Untuk pertanyaan "Apakah menggunakan teknik X bila memungkinkan praktik yang baik" satu-satunya jawaban yang benar adalah "tidak, gunakan teknik X kapan pun memungkinkan, tetapi kapan pun masuk akal" .
Doc Brown
Pilihan kapan menggunakan lambda (yang anonim) versus beberapa jenis implementasi fungsional lainnya (misalnya referensi metode) adalah pertanyaan yang sangat menarik. Saya memiliki kesempatan untuk bertemu Josh Bloch beberapa hari yang lalu, dan ini adalah salah satu poin yang dia bicarakan. Dari apa yang dia katakan saya mengerti dia berencana untuk menambahkan item baru ke edisi berikutnya Java Efektif berurusan dengan pertanyaan yang tepat ini.
Daniel Pryden
@DocBrown Itu seharusnya menjadi jawaban, bukan komentar.
gnasher729
1
@ gnasher729: pasti tidak.
Doc Brown

Jawaban:

116

Ada sejumlah kriteria yang harus membuat Anda mempertimbangkan untuk tidak menggunakan lambda:

  • Ukuran Semakin besar lambda, semakin sulit membuatnya mengikuti logika yang mengelilinginya.
  • Pengulangan Lebih baik untuk membuat fungsi bernama untuk logika berulang, meskipun tidak apa-apa untuk mengulang lambda yang sangat sederhana yang tersebar terpisah.
  • Penamaan Jika Anda bisa memikirkan nama semantik yang hebat, Anda harus menggunakannya, karena itu menambah banyak kejelasan pada kode Anda. Saya tidak berbicara nama-nama seperti priceIsOver100. x -> x.price > 100hanya sejelas nama itu. Maksud saya nama-nama seperti isEligibleVoteritu menggantikan daftar syarat yang panjang.
  • Nesting Nested lambdas sangat, sangat sulit dibaca.

Jangan berlebihan. Ingat, perangkat lunak mudah diubah. Jika ragu, tuliskan keduanya dan lihat mana yang lebih mudah dibaca.

Karl Bielefeldt
sumber
1
Penting juga untuk mempertimbangkan jumlah data yang akan diproses melalui lambda, karena lambdas agak tidak efisien pada sejumlah kecil pemrosesan data. Mereka benar-benar bersinar dalam paralelisasi set data besar.
CraigR8806
1
@ CraigR8806 Saya tidak berpikir kinerja menjadi perhatian di sini. Menggunakan fungsi anonim atau membuat kelas yang memperluas antarmuka fungsional harus kinerja yang sama bijaksana. Pertimbangan kinerja muncul ketika Anda berbicara tentang menggunakan aliran / fungsi tingkat tinggi api di atas loop gaya imperatif, tetapi dalam pertanyaan ini kami menggunakan antarmuka fungsional dalam kedua kasus hanya dengan sintaks yang berbeda.
puhlen
17
"Meskipun tidak apa-apa untuk mengulang lambda yang sangat sederhana yang tersebar terpisah" Hanya jika mereka tidak berubah bersama. Jika semuanya harus logika yang sama agar kode Anda benar, Anda harus menjadikannya semua metode yang sama sehingga perubahan hanya perlu dilakukan di satu tempat.
jpmc26
Secara keseluruhan ini adalah jawaban yang sangat bagus. Menyebutkan penggunaan referensi metode sebagai pengganti lambda akan menambal hanya lubang yang saya lihat dalam jawaban ini.
Morgen
3
Jika ragu, tuliskan keduanya dan tanyakan pada orang lain yang menjaga kode mana yang lebih mudah dibaca.
corsiKa
14

Tergantung. Setiap kali Anda menemukan diri Anda menggunakan lambda yang sama di tempat yang berbeda, Anda harus mempertimbangkan untuk mengimplementasikan kelas yang mengimplementasikan antarmuka. Tetapi jika Anda akan menggunakan kelas dalam anonim jika tidak saya pikir lambda jauh lebih baik.

Tohnmeister
sumber
6
Jangan lupa kemungkinan menggunakan referensi metode! Oracle telah menambahkan (dan menambahkan lebih banyak lagi di Java 9) metode statis dan metode default di semua tempat untuk alasan itu.
Jörg W Mittag
13

Saya mendukung jawaban Karl Bielefeldt, tetapi ingin memberikan tambahan singkat.

  • Debugging Beberapa pergulatan IDE dengan lingkup di dalam lambda, dan berjuang untuk menampilkan variabel anggota dalam konteks lambda. Meskipun mudah-mudahan situasi ini akan berubah di masa depan, itu bisa menjengkelkan untuk mempertahankan kode orang lain ketika penuh dengan lambda.
Jolleyboy
sumber
Jelas benar. NET. Tidak membuat saya menghindari lambda, tentu saja, tetapi saya jelas tidak merasa bersalah jika saya melakukan sesuatu yang lain sebagai gantinya.
user1172763
3
Jika itu masalahnya Anda menggunakan IDE yang salah. Baik IntelliJ dan Netbeans berjalan cukup baik di area tersebut.
David Foerster
1
@ user1172763 Dalam Visual Studio, jika Anda ingin melihat variabel anggota, saya telah menemukan bahwa Anda dapat melakukan perjalanan tumpukan panggilan sampai Anda sampai ke konteks lambda.
Zev Spitz
6

Akses ke variabel lokal dari cakupan terlampir

Jawaban yang diterima oleh Karl Bielefeldt benar. Saya dapat menambahkan satu lagi perbedaan:

  • Cakupan

Kode lambda yang bersarang di dalam metode di dalam kelas dapat mengakses variabel akhir yang efektif yang ditemukan dalam metode & kelas itu.

Membuat kelas yang mengimplementasikan antarmuka fungsional tidak memberi Anda akses langsung ke status kode panggilan.

Untuk mengutip Java Tutorial (penekanan):

Seperti kelas lokal dan anonim, ekspresi lambda dapat menangkap variabel; mereka memiliki akses yang sama ke variabel lokal dari cakupan terlampir . Namun, tidak seperti kelas lokal dan anonim, ekspresi lambda tidak memiliki masalah bayangan (lihat membayangi untuk informasi lebih lanjut). Ekspresi Lambda dibatasi secara leksikal. Ini berarti bahwa mereka tidak mewarisi nama apa pun dari supertipe atau memperkenalkan tingkat pelingkupan baru. Deklarasi dalam ekspresi lambda ditafsirkan seperti halnya mereka berada di lingkungan terlampir.

Jadi, sementara ada manfaat untuk mengeluarkan kode panjang dan menamainya, Anda harus mempertimbangkan itu terhadap kesederhanaan akses langsung ke keadaan metode & kelas terlampir.

Lihat:

Basil Bourque
sumber
4

Ini mungkin nit-picking, tetapi untuk semua poin bagus lainnya yang dibuat dalam jawaban lain, saya akan menambahkan:

Pilih Referensi Metode bila memungkinkan. Membandingkan:

employees.stream()
         .map(Employee::getName)
         .forEach(System.out::println);

melawan

employees.stream()
         .map(employee -> employee.getName())
         .forEach(employeeName -> System.out.println(employeeName));

Menggunakan referensi metode menghemat kebutuhan Anda untuk menamai argumen lambda, yang biasanya berlebihan dan / atau mengarah ke nama-nama malas seperti eatau x.

Matt McHenry
sumber
0

Berdasarkan jawaban Matt McHenry, mungkin ada hubungan antara lambda dan referensi metode di mana lambda dapat ditulis ulang sebagai serangkaian referensi metode. Sebagai contoh:

employees.stream()
         .forEach(employeeName -> System.out.println(employee.getName()));

vs.

employees.stream()
         .map(Employee::getName)
         .forEach(System.out::println);
LaFayette
sumber