Là sử dụng biểu thức Lambda bất cứ khi nào có thể trong java thực hành tốt?


52

Gần đây tôi đã thành thạo biểu thức Lambda được giới thiệu trong java 8. Tôi thấy rằng bất cứ khi nào tôi đang sử dụng giao diện chức năng, tôi có xu hướng luôn sử dụng biểu thức Lambda thay vì tạo một lớp thực hiện giao diện chức năng.

Đây có được coi là thực hành tốt? Hoặc là tình huống của họ khi sử dụng Lambda cho giao diện chức năng là không phù hợp?


122
Đối với câu hỏi "Có sử dụng kỹ thuật X bất cứ khi nào có thể thực hành tốt" câu trả lời đúng duy nhất là "không, sử dụng kỹ thuật X không phải bất cứ khi nào có thể, nhưng bất cứ khi nào hợp lý" .
Doc Brown

Lựa chọn khi nào nên sử dụng lambda (ẩn danh) so với một số loại thực thi chức năng khác (ví dụ: tham chiếu phương thức) là một câu hỏi thực sự thú vị. Tôi đã có cơ hội gặp Josh Bloch vài ngày trước, và đây là một trong những điểm anh ấy nói. Từ những gì anh ấy nói, tôi hiểu rằng anh ấy dự định thêm một mục mới vào phiên bản tiếp theo của Java hiệu quả để giải quyết câu hỏi chính xác này.
Daniel Pryden

@DocBrown Đó phải là một câu trả lời, không phải là một bình luận.
gnasher729

1
@ gnasher729: chắc chắn là không.
Doc Brown

Câu trả lời:


116

Có một số tiêu chí khiến bạn cân nhắc không sử dụng lambda:

  • Kích thước Một lambda càng lớn, càng khó thực hiện theo logic xung quanh nó.
  • Lặp lại Tốt hơn là tạo một hàm được đặt tên cho logic lặp lại, mặc dù việc lặp lại các lambdas rất đơn giản được trải rộng là tốt hơn.
  • Đặt tên Nếu bạn có thể nghĩ ra một tên ngữ nghĩa tuyệt vời, bạn nên sử dụng tên đó thay vào đó, vì nó thêm rất nhiều sự rõ ràng vào mã của bạn. Tôi không nói tên như thế priceIsOver100. x -> x.price > 100chỉ rõ ràng như tên đó. Ý tôi là những cái tên như isEligibleVoterthế thay thế một danh sách dài các điều kiện.
  • Nestdas Nested Nested thực sự, thực sự rất khó đọc.

Đừng quá nhiệt tình. Hãy nhớ rằng, phần mềm dễ dàng thay đổi. Khi nghi ngờ, hãy viết cả hai cách và xem cái nào dễ đọc hơn.


1
Điều quan trọng là phải xem xét lượng dữ liệu được xử lý thông qua lambda, vì lambdas khá kém hiệu quả với số lượng nhỏ xử lý dữ liệu. Họ thực sự tỏa sáng song song của các tập dữ liệu lớn.
CraigR8806

1
@ CraigR8806 Tôi không nghĩ hiệu suất là một mối quan tâm ở đây. Sử dụng một hàm ẩn danh hoặc tạo một lớp mở rộng giao diện chức năng sẽ là cùng một hiệu suất khôn ngoan. Các cân nhắc về hiệu năng xuất hiện khi bạn nói về việc sử dụng api luồng / hàm bậc cao hơn trong một vòng lặp kiểu bắt buộc, nhưng trong câu hỏi này, chúng tôi đang sử dụng các giao diện chức năng trong cả hai trường hợp chỉ với cú pháp khác nhau.
puhlen

17
"Mặc dù không thể lặp lại những lambdas rất đơn giản được phân tán" Chỉ khi chúng không nhất thiết phải thay đổi cùng nhau. Nếu tất cả chúng phải cùng logic để mã của bạn chính xác, bạn nên làm cho tất cả chúng thực sự giống nhau để các thay đổi chỉ cần được thực hiện ở một nơi.
jpmc26

Nhìn chung đây là một câu trả lời thực sự tốt. Đề cập đến việc sử dụng một tham chiếu phương thức như là một thay thế cho lambda sẽ vá vào lỗ hổng duy nhất tôi thấy trong câu trả lời này.
Morgen

3
Khi nghi ngờ, hãy viết cả hai cách và hỏi người khác duy trì mã dễ đọc hơn.
corsiKa

14

Nó phụ thuộc. Bất cứ khi nào bạn thấy mình sử dụng cùng lambda ở những nơi khác nhau, bạn nên xem xét việc thực hiện một lớp thực hiện giao diện. Nhưng nếu bạn đã sử dụng một lớp bên trong ẩn danh nếu không tôi nghĩ rằng lambda tốt hơn nhiều.


6
Đừng quên khả năng sử dụng tham chiếu phương pháp! Oracle đã thêm (và đang bổ sung nhiều hơn nữa trong Java 9) các phương thức tĩnh và các phương thức mặc định ở khắp mọi nơi vì chính xác lý do đó.
Jörg W Mittag

13

Tôi ủng hộ câu trả lời của Karl Bielefeldt, nhưng muốn cung cấp một bổ sung ngắn gọn.

  • Gỡ lỗi Một số cuộc đấu tranh của IDE với phạm vi bên trong lambda và đấu tranh để hiển thị các biến thành viên bên trong bối cảnh của lambda. Mặc dù hy vọng tình huống này sẽ thay đổi, nhưng việc duy trì mã của người khác có thể gây khó chịu khi bị vấy bẩn bởi lambdas.

Chắc chắn đúng với .NET. Không làm cho tôi tránh lambdas, nhất thiết, nhưng tôi chắc chắn không cảm thấy tội lỗi nếu tôi làm điều gì khác thay thế.
1172763

3
Nếu đó là trường hợp bạn đang sử dụng IDE sai. Cả IntelliJ và Netbeans đều làm khá tốt trong lĩnh vực cụ thể đó.
David foerster

1
@ user1172763 Trong Visual Studio, nếu bạn muốn xem các biến thành viên, tôi thấy rằng bạn có thể đi lên ngăn xếp cuộc gọi cho đến khi bạn đến bối cảnh của lambda.
Zev Spitz

6

Truy cập vào các biến cục bộ của phạm vi kèm theo

Câu trả lời được chấp nhận bởi Karl Bielefeldt là chính xác. Tôi có thể thêm một phân biệt nữa:

  • Phạm vi

Mã lambda được lồng bên trong một phương thức bên trong một lớp có thể truy cập bất kỳ biến cuối cùng có hiệu quả nào được tìm thấy trong phương thức & lớp đó.

Tạo một lớp thực hiện giao diện chức năng không cung cấp cho bạn quyền truy cập trực tiếp như vậy vào trạng thái của mã gọi.

Để trích dẫn Hướng dẫn Java (nhấn mạnh của tôi):

Giống như các lớp cục bộ và ẩn danh, các biểu thức lambda có thể nắm bắt các biến; họ có cùng quyền truy cập vào các biến cục bộ của phạm vi kèm theo . Tuy nhiên, không giống như các lớp cục bộ và ẩn danh, các biểu thức lambda không có bất kỳ vấn đề đổ bóng nào (xem Shadowing để biết thêm thông tin). Biểu thức Lambda có phạm vi từ vựng. Điều này có nghĩa là họ không kế thừa bất kỳ tên nào từ một siêu kiểu hoặc giới thiệu một cấp độ mới của phạm vi. Các khai báo trong biểu thức lambda được diễn giải giống như chúng ở trong môi trường kèm theo.

Vì vậy, trong khi có những lợi ích khi rút mã dài và đặt tên mã, bạn phải cân nhắc điều đó với sự đơn giản của việc truy cập trực tiếp vào trạng thái của phương thức & lớp kèm theo.

Xem:


4

Điều này có thể là chọn nit, nhưng với tất cả các điểm tuyệt vời khác được đưa ra trong các câu trả lời khác, tôi sẽ thêm:

Thích phương pháp tham khảo khi có thể. So sánh:

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

đấu với

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

Sử dụng tham chiếu phương thức giúp bạn tiết kiệm được yêu cầu đặt tên (các) đối số của lambda, thường là dư thừa và / hoặc dẫn đến các tên lười biếng như ehoặc x.


0

Dựa trên câu trả lời của Matt McHenry, có thể có mối quan hệ giữa lambda và các tham chiếu phương thức trong đó lambda có thể được viết lại dưới dạng một loạt các tham chiếu phương thức. Ví dụ:

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

đấu với

employees.stream()
         .map(Employee::getName)
         .forEach(System.out::println);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.