Java iyi uygulamada mümkün olduğunda Lambda ifadeleri kullanmak mı?


52

Java 8'de tanıtılan Lambda ifadesinde usta oldum. İşlevsel bir arayüz kullanırken, fonksiyonel arayüzü uygulayan bir sınıf oluşturmak yerine her zaman bir Lambda ifadesi kullanma eğiliminde olduğumu fark ettim.

Bu iyi bir uygulama olarak mı kabul edilir? Yoksa fonksiyonel bir arayüz için bir Lambda kullanmanın uygun olmadığı durumlar mı?


122
Sorusuna "olası iyi uygulama her tekniği X kullanıyor" tek doğru cevap "hayır, kullanım tekniği X değil mümkünse, ama ne zaman mantıklı" .
Doktor Brown

Bir lambda ne zaman kullanılacağının (diğer adıyla) ne tür bir işlevsel uygulamaya (örneğin bir yöntem referansı) karşı seçimi gerçekten ilginç bir sorudur. Birkaç gün önce Josh Bloch'la tanışma fırsatım oldu ve bu konuştuğu noktalardan biriydi. Anladığıma göre , bu tam soruyla ilgilenen bir sonraki Effective Java sürümüne yeni bir ürün eklemeyi planlıyor .
Daniel Pryden

@DocBrown Bu bir cevap olmalı, yorum değil.
gnasher729

1
@ gnasher729: kesinlikle değil.
Doktor Brown

Yanıtlar:


116

Bir lambda kullanmamayı düşünmeniz gereken bazı kriterler vardır:

  • Boyut Bir lambda büyüdükçe, etrafını saran mantığı izlemesini zorlaştırır.
  • Tekrarlama Tekrarlanan mantık için adlandırılmış bir fonksiyon oluşturmak daha iyidir, ancak birbirinden ayrılan çok basit lambdaları tekrarlamakta bir sakınca yoktur.
  • Adlandırma Harika bir anlamsal ad düşünebiliyorsanız, kodunuza çok açıklık getirdiği için bunu kullanmalısınız. Ben gibi isimlerden bahsetmiyorum priceIsOver100. x -> x.price > 100bu isim kadar açık. Bunun gibi isimler isEligibleVoteruzun bir şartlar listesinin yerine geçiyor.
  • Yuvalama Yuvalama lambdaları okumak gerçekten çok zor.

Denize düşme. Unutmayın, yazılım kolayca değiştirilir. Şüphe duyduğunuzda, her iki yolu da yazın ve hangisinin daha kolay okunabileceğini görün.


1
Ayrıca lambda yoluyla işlenecek veri miktarını göz önünde bulundurmak da önemlidir, çünkü lambdalar küçük miktarlarda veri işlemede oldukça yetersizdir. Büyük veri setlerinin paralelleşmesinde gerçekten parlıyorlar.
CraigR8806,

1
@ CraigR8806 Performansın burada bir endişe olduğunu sanmıyorum. Anonim bir işlev kullanmak veya işlevsel arabirimi genişleten bir sınıf oluşturmak aynı performans açısından akıllıca olmalıdır. Zorunlu bir stil döngüsünde akışları / üst düzey işlevler api'yi kullanmaktan bahsederken performansla ilgili düşünceler geliyor, ancak bu soruda her iki durumda da sadece farklı sözdizimine sahip işlevsel arayüzleri kullanıyoruz.
puhlen

17
“Ayrı dağılmış çok basit lambdaları tekrar etmek doğru olsa da” Sadece birlikte değişmeleri gerekmiyorsa. Onlar ise gerekir kodunuzu doğru olduğu için hepsi aynı mantık olması değişiklikler sadece tek bir yerde yapılması gerekiyor ki, aslında aynı yöntem hepsini yapmalıdır.
jpmc26

Genel olarak bu gerçekten iyi bir cevap. Bir lambdaya alternatif olarak bir yöntem referansının kullanılmasından bahsetmek, bu cevapta gördüğüm sadece deliğe yarayacaktır.
Morgen

3
Şüphe duyduğunuzda, her iki şekilde de yazınız ve okunması daha kolay olan kodu koruyan başkalarına sorunuz.
corsiKa

14

Değişir. Kendinizi aynı lambda'yı farklı yerlerde kullanırken bulduğunuzda, arayüzü uygulayan bir sınıf uygulamayı düşünmelisiniz. Ama eğer isimsiz bir iç sınıf kullanırsanız, bir lambda'nın çok daha iyi olduğunu düşünüyorum.


6
Bir yöntem referansı kullanma olasılığını unutmayın! Oracle, tam da bu sebepten ötürü her yerde statik yöntemler ve varsayılan yöntemler ekledi (ve Java 9'a daha da fazlasını ekliyor).
Jörg W Mittag

13

Karl Bielefeldt'in cevabını destekliyorum ama kısa bir ekleme yapmak istiyorum.

  • Hata ayıklama Bazı IDE'lerin bir lambda içindeki kapsamla mücadelesi ve üye değişkenleri bir lambda bağlamında gösterme mücadelesi. Umarım bu durum çizgiyi aşar, ancak bir başkasının kodunu Lambda'larla karıştırıldığında korumak rahatsız edici olabilir.

Kesinlikle .NET. Mutlaka, kuzulardan kaçınmamı sağlamıyor, ama bunun yerine başka bir şey yaparsam kesinlikle hiçbir suçluluk hissetmiyorum.
user1172763

3
Bu durumda yanlış IDE kullanıyorsunuz. Hem IntelliJ hem de NetBeans bu alanda oldukça başarılı.
David Foerster

1
@ user1172763 Visual Studio'da, üye değişkenlerini görüntülemek istiyorsanız, lambda bağlamına gelene kadar çağrı yığınını yukarı taşıyabileceğinizi gördüm.
Zev Spitz,

6

Ek kapsamının yerel değişkenlerine erişim

Karl Bielefeldt'in kabul ettiği cevap doğru. Bir fark daha ekleyebilirim:

  • Dürbün

Bir sınıf içinde bir yöntemin içine yerleştirilmiş lambda kodu, bu yöntem ve sınıf içinde bulunan herhangi bir etkin son değişkene erişebilir .

İşlevsel arayüzü uygulayan bir sınıf oluşturmak, çağıran kodun durumuna doğrudan erişiminizi sağlamaz.

Java Eğitimini alıntı yapmak (benimkine vurgu yapmak):

Yerel ve anonim sınıflar gibi, lambda ifadeleri değişkenleri yakalayabilir; Ek kapsamının yerel değişkenlerine aynı erişime sahiptirler . Ancak, yerel ve adsız sınıfların aksine, lambda ifadelerinin herhangi bir gölgeleme sorunu yoktur (daha fazla bilgi için bkz. Gölgeleme). Lambda ifadeleri sözcüksel olarak kapsam dahilindedir. Bu, bir süper tipten hiçbir isim almadıkları ya da yeni bir kapsam belirleme anlamına gelmedikleri anlamına gelir. Bir lambda ifadesindeki bildirimler, tıpkı kapalı ortamda olduğu gibi yorumlanır.

Bu nedenle, uzun kodları çıkarmanın ve adlandırmanın faydaları varken, bunu ekteki yöntem ve sınıfın durumuna doğrudan erişimin basitliğine karşı tartmalısınız.

Görmek:


4

Bu nitrat toplama olabilir, ancak diğer cevaplarda verilen diğer tüm mükemmel noktalara şunu ekleyeceğim:

Mümkünse Yöntem Referanslarını tercih edin . Karşılaştırmak:

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

e karşı

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

Bir yöntem başvuru kullanmak size genellikle gereksiz ve / veya benzeri tembel isimlerle yol açar lambda argümanını (ler), isim ihtiyacını kaydeder eveya x.


0

Matt McHenry'nin cevabını temel alarak, lambda ile lambda'nın bir dizi yöntem referansı olarak yeniden yazılabileceği yöntem referansları arasında bir ilişki olabilir. Örneğin:

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

vs

employees.stream()
         .map(Employee::getName)
         .forEach(System.out::println);
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.