Tôi đang sử dụng AdoptOpenJDK jdk81212-b04
trên Ubuntu Linux, chạy trên Eclipse 4.13. Tôi có một phương pháp trong Swing tạo ra lambda bên trong lambda; cả hai có thể được gọi trên các chủ đề riêng biệt. Nó trông như thế này (mã giả):
private SwingAction createAction(final Data payload) {
System.out.println(System.identityHashCode(payload));
return new SwingAction(() -> {
System.out.println(System.identityHashCode(payload));
//do stuff
//show an "are you sure?" modal dialog and get a response
//show a file selection dialog
//when the dialog completes, create a worker and show a status:
showStatusDialogWithWorker(() -> new SwingWorker() {
protected String doInBackground() {
save(payload);
}
});
Bạn có thể thấy rằng lambdas sâu vài lớp và cuối cùng "tải trọng" được chụp sẽ được lưu vào một tệp, ít nhiều.
Nhưng trước khi xem xét các lớp và các chủ đề, chúng ta hãy đi thẳng vào vấn đề:
- Lần đầu tiên tôi gọi
createAction()
, haiSystem.out.println()
phương thức in mã băm chính xác giống nhau, chỉ ra rằng tải trọng bị bắt bên trong lambda giống như tôi đã chuyển quacreateAction()
. - Nếu sau này tôi gọi
createAction()
với một trọng tải khác nhau, haiSystem.out.println()
giá trị được in sẽ khác nhau! Cụ thể, dòng thứ hai được in luôn chỉ ra cùng một giá trị đã được in ở bước 1 !!
Tôi có thể lặp lại điều này nhiều lần; tải trọng thực tế được thông qua sẽ tiếp tục nhận được mã băm nhận dạng khác nhau, trong khi dòng thứ hai được in (bên trong lambda) sẽ giữ nguyên! Cuối cùng, một cái gì đó sẽ nhấp và đột nhiên các số sẽ giống nhau một lần nữa, nhưng sau đó chúng sẽ phân kỳ với cùng một hành vi.
Có phải Java bằng cách nào đó lưu trữ lambda, cũng như đối số sẽ xảy ra với lambda? Nhưng làm thế nào là điều này có thể? Cuộc payload
tranh cãi đã được đánh dấu final
, và bên cạnh đó, việc bắt giữ lambda dù sao cũng phải là trận chung kết hiệu quả!
- Có một lỗi Java 8 không nhận ra lambda không nên được lưu vào bộ đệm nếu biến bị bắt là một số lambdas sâu?
- Có một lỗi Java 8 đang lưu trữ các đối số lambdas và lambda trên các luồng không?
- Hoặc có điều gì đó mà tôi không hiểu về lambda đối số phương thức so với biến cục bộ của phương thức?
Lần thử đầu tiên không thành công
Hôm qua tôi nghĩ rằng tôi có thể ngăn chặn hành vi này chỉ bằng cách chụp tham số phương thức cục bộ trên ngăn xếp phương thức:
private SwingAction createAction(final Data payload) {
final Data theRealPayload = payload;
System.out.println(System.identityHashCode(theRealPayload));
return new SwingAction(() -> {
System.out.println(System.identityHashCode(theRealPayload));
//do stuff
//show an "are you sure?" modal dialog and get a response
//show a file selection dialog
//when the dialog completes, create a worker and show a status:
showStatusDialogWithWorker(() -> new SwingWorker() {
protected String doInBackground() {
save(theRealPayload);
}
});
Với dòng đơn đó Data theRealPayload = payload
, nếu tôi sử dụng theRealPayload
thay vì payload
đột nhiên thì lỗi không còn xuất hiện nữa và mỗi lần tôi gọi createAction()
, hai dòng in chỉ ra chính xác cùng một thể hiện của biến bị bắt.
Tuy nhiên hôm nay cách giải quyết này đã ngừng hoạt động.
Vấn đề địa chỉ sửa lỗi riêng biệt; Nhưng tại sao?
Tôi tìm thấy một lỗi riêng biệt đang ném một ngoại lệ vào bên trong showStatusDialogWithWorker()
. Về cơ bản showStatusDialogWithWorker()
được cho là tạo worker (trong lambda đã qua) và hiển thị hộp thoại trạng thái cho đến khi worker hoàn thành. Có một lỗi sẽ tạo ra worker chính xác, nhưng không tạo được hộp thoại, ném ra một ngoại lệ sẽ nổi bong bóng và không bao giờ bị bắt. Tôi đã sửa lỗi này để showStatusDialogWithWorker()
hộp thoại hiển thị thành công khi worker đang chạy và sau đó đóng nó sau khi worker hoàn thành. Bây giờ tôi không còn có thể tái tạo vấn đề chụp lambda nữa.
Nhưng tại sao một cái gì đó bên trong showStatusDialogWithWorker()
liên quan đến vấn đề? Khi tôi đang in ra System.identityHashCode()
bên ngoài và bên trong lambda, và các giá trị khác nhau, điều này đã xảy ra trước khi showStatusDialogWithWorker()
được gọi và trước khi ngoại lệ được ném ra. Tại sao một ngoại lệ sau này làm cho một sự khác biệt?
Bên cạnh đó, câu hỏi cơ bản vẫn là: làm thế nào thậm chí có thể một final
tham số được truyền bởi một phương thức và được lambda bắt giữ có thể thay đổi?
final
đối số phương thức có thể thay đổi bên ngoài và bên trong lambda? Và tại sao việc bắt biến là một final
biến cục bộ trên ngăn xếp sẽ tạo ra sự khác biệt?
final
biến trên ngăn xếp không còn khắc phục được vấn đề. Tôi tiếp tục điều tra.
SwingAction
chính nó. Bạn làm gì với SwingAction
trả về từ phương thức?