Là một biểu thức lambda một cái gì đó nhiều hơn một lớp bên trong vô danh với một phương thức duy nhất?


40

Có một sự cường điệu mới với các biểu thức lambda được chờ đợi từ lâu trong Java 8; cứ sau 3 ngày lại có một bài báo khác xuất hiện với họ về việc họ tuyệt như thế nào.

Theo như tôi đã hiểu thì một biểu thức lambda không gì khác hơn là một lớp bên trong ẩn danh với một phương thức duy nhất (ít nhất là ở cấp mã byte). Bên cạnh đó, nó đi kèm với một tính năng hay khác - suy luận kiểu nhưng tôi tin rằng tương đương với điều này có thể đạt được với các khái quát ở một mức độ nào đó (tất nhiên không phải theo cách gọn gàng như với các biểu thức lambda).

Biết điều này, các biểu thức lambda sẽ mang lại một cái gì đó nhiều hơn chỉ là một cú pháp cú pháp trong Java? Tôi có thể tạo các lớp mạnh hơn và linh hoạt hơn hoặc các cấu trúc hướng đối tượng khác bằng các biểu thức lambda không thể được xây dựng với các tính năng ngôn ngữ hiện tại không?


30
Khi một ngôn ngữ hoàn chỉnh, mọi tính năng được thêm vào về mặt lý thuyết có thể được mô tả là "đường cú pháp".
Philipp

34
@Philipp: Điều đó sai. Đặc điểm xác định của đường cú pháp là việc giải nén hoàn toàn cục bộ và không thay đổi cấu trúc toàn cầu của chương trình. Có rất nhiều tính năng không thể thực hiện được chỉ với các biến đổi cục bộ. Ví dụ: nếu bạn sử dụng một ngôn ngữ giả định giống hệt với Java nhưng với các lệnh gọi đuôi thích hợp, sẽ không thể thực hiện các lệnh gọi đuôi thành Java "thuần túy" mà không chuyển đổi toàn bộ chương trình.
Jörg W Mittag

2
@Philipp: Thật không may, chỉ có một upvote cho các bình luận, nếu không tôi sẽ upvote bình luận của Joerg 1000 lần. Không, thật sai lầm khi bất kỳ tính năng nào có thể được mô tả là đường cú pháp. Đường cú pháp không thêm ngữ nghĩa mới. Trong thực tế, AFAIK các lambdas Java được đề xuất KHÔNG phải là cú pháp đường cho các lớp bên trong ẩn danh đặc biệt vì chúng có ngữ nghĩa khác nhau.
Giorgio

1
@Giorgio: và họ có những ngữ nghĩa khác nhau gì?
user102008

2
@Giorgio: Có, nhưng việc chuyển đổi thisthành OuterClass.thismột phần của quá trình khử biểu thức lambda thành lớp ẩn danh.
user102008

Câu trả lời:


47

tl; dr: trong khi chủ yếu là đường cú pháp, cú pháp đẹp hơn đó tạo ra nhiều điều thiết thực được sử dụng để kết thúc trong các dòng dấu ngoặc và dấu ngoặc dài vô tận, không thể đọc được.

Chà, thực ra thì ngược lại vì lambdas già hơn Java rất nhiều . Các lớp bên trong ẩn danh với một phương thức duy nhất là (là) Java gần nhất đã đến lambdas. Đó là một xấp xỉ "đủ tốt" trong một thời gian, nhưng có một cú pháp rất khó chịu.

Nhìn bề ngoài, Java 8 lambdas dường như không nhiều hơn so với đường cú pháp, nhưng khi bạn nhìn bên dưới bề mặt, bạn thấy hàng tấn trừu tượng thú vị. Ví dụ, đặc tả JVM đối xử với lambda hoàn toàn khác với một đối tượng "thật" và trong khi bạn có thể xử lý chúng như thể chúng ở đâu trong các đối tượng, thì JVM không bắt buộc phải thực hiện chúng như vậy.

Nhưng trong khi tất cả các mánh khóe kỹ thuật đó đều thú vị và phù hợp (vì nó cho phép tối ưu hóa trong tương lai trong JVM!), Lợi ích thực sự là "chỉ" phần đường cú pháp.

Những gì dễ đọc hơn:

myCollection.map(new Mapper<String,String>() {
  public String map(String input) {
    return new StringBuilder(input).reverse().toString();
  }
});

hoặc là:

myCollection.map(element -> new StringBuilder(element).reverse().toString());

hoặc (sử dụng tay cầm phương thức thay vì lambda):

myCollection.map(String::toUpperCase);

Thực tế là cuối cùng bạn có thể diễn đạt một cách ngắn gọn mà trước đó là 5 dòng mã (trong đó 3 dòng hoàn toàn nhàm chán) mang đến một sự thay đổi thực sự của những gì thực tế (nhưng không phải là những gì có thể, được cấp).


1
Tôi hoàn toàn đồng ý với phần cú pháp đường; câu hỏi của tôi là về các cấu trúc hướng đối tượng mới chỉ có thể với các biểu thức lambda; sau tất cả Java là một ngôn ngữ hướng đối tượng. Hãy suy nghĩ so với các khái quát và cách họ mua nhiều tính linh hoạt trong Java, tính linh hoạt không thể thực hiện được nếu không có chúng.
m3th0dman

7
@ m3th0dman: thực sự thuốc generic không có khả năng mới. A Listcó thể giữ bất kỳ đối tượng nào, một List<String>"chỉ" có thể giữ các chuỗi. Generics đã thêm một hạn chế (và tùy chọn để chính thức hóa các hạn chế!), Không phải là một bổ sung sức mạnh. Khá nhiều thứ kể từ khi Java 1.1 là cú pháp đường. Và đó không hẳn là một điều xấu.
Joachim Sauer

3
@ m3th0dman: Thật ra thuốc generic không có khả năng mới, vì trong Java chúng chỉ là một đường cú pháp. A List<String>chỉ là một Listvới cast để Stringthêm vào xung quanh một số giá trị trả về. Tuy nhiên, chúng cực kỳ hữu ích và làm cho ngôn ngữ thuận tiện hơn nhiều để viết. Lambdas là trường hợp tương tự.
Jan Hudec

3
Trên thực tế, nó không chỉ là đường cú pháp. Phần lớn các chức năng được triển khai với tư cách là một công dân hạng nhất trong JVM thông qua việc sử dụng mã byte được kích hoạt + một số tiến bộ khá lớn cho hệ thống suy luận Kiểu. Nó không được thực hiện như các lớp bên trong vô danh.
Martijn Verburg

1
@JanHudec: tốt, chúng hơi nhiều so với đường cú pháp, bởi vì trình biên dịch thực sự tôn trọng chúng. Thời gian chạy không quan tâm đến họ, đó là sự thật.
Joachim Sauer

14

Đối với Java, vâng, không có gì khác hơn là cách tốt hơn để tạo một lớp bên trong ẩn danh. Điều này là do quyết định cơ bản trong java rằng mọi bit của mã byte phải sống trong một lớp cụ thể, hiện không thể thay đổi sau nhiều thập kỷ mã kế thừa để xem xét.

Tuy nhiên, đó không phải là những gì lambda thực sự nói về. Trong các hình thức mà chúng là các khái niệm bản địa chứ không phải là một mâu thuẫn nhảy lên trên băng đảng, lambdas là các khối xây dựng cơ bản; cả cú pháp và thái độ của mọi người đối với họ đều rất khác nhau. Ví dụ về một hàm đệ quy ẩn danh được tạo ra hoàn toàn từ lambdas trong Cấu trúc và Giải thích các Chương trình Máy tính có khả năng thay đổi toàn bộ quan niệm của bạn về tính toán. (Tuy nhiên, tôi khá chắc chắn rằng cách học lập trình chỉ là bí truyền để trở thành một câu chuyện thành công chủ đạo.)


1
Học hỏi rằng tất cả mọi thứ có thể được thực hiện trong điều khoản của lambdas là rất hướng dẫn và không "bí truyền" theo ý kiến ​​của tôi. (Tuy nhiên, tôi đoán hầu hết các "lập trình viên" không quan tâm đến lý thuyết CS thú vị.) Điều đó không có nghĩa là lambdas là đặc biệt; nó chỉ có nghĩa là lambdas là một loại cấu trúc phổ quát. Có những người khác, như tổ hợp SKI. Lambdas là các khối xây dựng cơ bản trong các mô hình chức năng, nhưng có lẽ một cái gì đó khác có thể là nền tảng trong một mô hình khác.
user102008

+1 cho "mâu thuẫn giữa các nhóm nhảy": Tôi thường tự hỏi tại sao một số ngôn ngữ tiếp tục thay đổi để bắt chước các ngôn ngữ phổ biến khác và tại sao các lập trình viên lại chấp nhận nó. Tôi thích lambdas và sử dụng chúng rất nhiều trong Scala, Lisp, Haskell, nhưng trong Java hoặc C ++, họ cảm thấy như một suy nghĩ lại bị cuốn vào ngôn ngữ chỉ vì chúng trở nên phổ biến trong các ngôn ngữ khác.
Giorgio

2

Vâng, nó chỉ là một đường cú pháp, theo nghĩa là bất cứ nơi nào bạn viết lambda, bạn có thể viết lại biểu thức đó dưới dạng một biểu thức lớp bên trong ẩn danh với một phương thức, trong đó lớp thực hiện giao diện chức năng được suy ra cho bối cảnh của lambda và nó sẽ chính xác tương đương về mặt ngữ nghĩa. Và bạn có thể làm điều này bằng cách thay thế biểu thức lambda bằng biểu thức lớp ẩn danh, mà không thay đổi bất kỳ biểu thức hoặc dòng mã nào khác.


1
Tại sao downvote? những gì tôi nói là hoàn toàn chính xác
user102008

Những gì bạn viết có vẻ là một đề xuất hợp lý cho Java lambdas nhưng đề xuất này đã bị loại bỏ để ủng hộ một cái khác ( doanduyhai.wordpress.com/2012/07/12/ trên ).
Giorgio

1
@Giorgio: Tôi không "đề xuất" gì cả. Chính xác thì bạn không đồng ý với những gì tôi nói? Có, bên trong biểu thức sẽ trông khác nhau, ví dụ: chúng tôi sẽ thêm một phương thức và có, thissẽ cần phải được chuyển đổi thành OuterClass.this. Điều đó không mâu thuẫn với những gì tôi đã nói - mọi biểu thức lambda có thể được chuyển đổi thành biểu thức lớp ẩn danh tương đương về mặt ngữ nghĩa mà không thay đổi bất cứ điều gì bên ngoài biểu thức đó . Tôi đã không nói rằng bên trong sẽ không thay đổi.
user102008

3
Những gì bạn nói là không chính xác. Các ngữ nghĩa của lớp bên trong lambda và anon không hoàn toàn giống nhau (mặc dù chúng tương tự nhau). Ngoài đỉnh đầu của tôi, ý nghĩa của ý nghĩa của sự thay đổi này ; và các lớp bên trong anon luôn dẫn đến một thể hiện mới của một đối tượng, trong khi lambda có thể hoặc không.
Stuart Marks

1
@StuartMarks: Không, bạn đã không đọc câu trả lời của tôi. Tôi đã không nói rằng mỗi người trong số họ có thể làm mọi thứ khác. Tôi đã nói rằng một lambda có một lớp ẩn danh tương đương giống nhau về mặt ngữ nghĩa. Như tôi đã nói trong các bình luận, thistrong lambda là một phần của đường cú pháp, và không phải là một sự khác biệt về ngữ nghĩa. Và về sự khác biệt trong việc thực hiện, thực hiện! = Ngữ nghĩa. Về sự khác biệt trong danh tính đối tượng; danh tính đối tượng là vô cùng tiếp tuyến với chức năng của lambdas; không ai kiểm tra danh tính đối tượng của các lớp lambdas / anon vì nó không hữu ích.
user102008

1

Joachim Sauer đã làm rất tốt khi trả lời câu hỏi của bạn nhưng chỉ nói bóng gió về điều gì đó tôi coi là quan trọng. Vì Lambdas không có lớp nên chúng cũng không được biên dịch như vậy. Tất cả các lớp bên trong ẩn danh dẫn đến việc tạo tệp. Class mà lớp này phải được tải bởi ClassLoader. Vì vậy, sử dụng Lambdas thay vào đó không chỉ làm cho mã của bạn đẹp hơn mà còn giảm kích thước mã được biên dịch, dung lượng bộ nhớ của ClassLoader và thời gian cần thiết để chuyển các bit từ ổ cứng của bạn.


-1

Không họ không.

Một cách khác để đặt nó là lambdas là một cách không hướng đối tượng, tuy nhiên ngắn gọn, để đạt được điều tương tự mà một lớp ẩn danh hoặc, theo sở thích của tôi, lớp bên trong sẽ đạt được theo cách hướng đối tượng.


1
Lập trình hàm có thể hoàn toàn trực giao với lập trình hướng đối tượng (xem: Scala và F #). Điều này đọc giống như ý kiến ​​của một người chỉ đơn giản là không thích lập trình chức năng trên nguyên tắc.
KChaloux

1
@KChaloux - Không phải là một người ghét lập trình chức năng. Câu trả lời được viết bởi một người thích dùng búa VÀ tuốc nơ vít hơn là dùng búa. Lập trình OO là một mô hình tốt, cũng như lập trình chức năng. Sở thích của tôi trong một ngôn ngữ là cho nó rõ ràng về ý định của nó. Lambdas Tôi cảm thấy lầy lội về bản chất OO khác của Java. Java không được thiết kế để trở thành một ngôn ngữ chức năng. Con người cần cấu trúc để làm công việc tốt nhất của họ.
Guido Anselmi
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.