2008-11-25 7 views
57

Trong Java, bạn có thể đủ điều kiện biến cục bộ và tham số phương thức với từ khóa cuối cùng.Tại sao một đánh dấu các biến cục bộ và các tham số của phương thức là "cuối cùng" trong Java?

public static void foo(final int x) { 
    final String qwerty = "bar"; 
} 

Làm như vậy sẽ không thể gán lại x và qwerty trong phần thân của phương thức.

Thực hành này thúc đẩy mã của bạn theo hướng bất biến, thường được coi là dấu cộng. Nhưng, nó cũng có xu hướng lộn xộn lên mã với "cuối cùng" hiển thị ở khắp mọi nơi. Ý kiến ​​của bạn về từ khóa cuối cùng cho các biến địa phương và các tham số phương thức trong Java là gì?

+2

Xem http://stackoverflow.com/questions/266806/is-there-any-performance-reason-to-declare-method-parameters-final-in-java#266981 và http://stackoverflow.com/ câu hỏi/137868/sử dụng-sửa đổi cuối cùng-bất cứ khi nào áp dụng-in-java và http://stackoverflow.com/questions/154314/when-to-use-final – Robin

Trả lời

53

Bạn nên cố gắng thực hiện việc này, bất cứ khi nào phù hợp. Bên cạnh việc phục vụ để cảnh báo bạn khi bạn "vô tình" cố sửa đổi một giá trị, nó cung cấp thông tin cho trình biên dịch có thể dẫn đến tối ưu hóa tốt hơn tệp lớp. Đây là một trong những điểm trong cuốn sách, "Hardcore Java" của Robert Simmons, Jr. Trong thực tế, cuốn sách dành tất cả chương thứ hai của nó về việc sử dụng cuối cùng để thúc đẩy tối ưu hóa và ngăn chặn các lỗi logic. Các công cụ phân tích tĩnh như PMD và SA tích hợp của Eclipse gắn cờ các loại trường hợp này vì lý do này.

+8

+1: Chỉ một phần bổ sung - 'final' cũng truyền đạt _intent_ của tác giả, cả về tham số lẫn biến cục bộ. –

+3

Tôi đang đọc Hardcore Java ngay bây giờ. Tôi nghĩ rằng tôi biết java ........... – WolfmanDragon

+2

Bạn có chắc chắn các tham số địa phương/phương thức cuối cùng có thể khiến trình biên dịch tối ưu hóa tốt hơn không? Đối với một điểm truy cập, hãy tưởng tượng nếu bạn khai báo một biến cục bộ 'int x = 2'. Nếu bạn ** vô điều kiện ** không bao giờ gán lại 'x', trình biên dịch ** sẽ ** biết điều này, bởi vì bằng cách quét toàn bộ mã cơ thể và thấy rằng' x' không thể được gán lại bất kể điều kiện nào, nó suy ra rằng 'x 'sẽ không bao giờ được sửa đổi ... –

27

Ý kiến ​​cá nhân của tôi là nó lãng phí thời gian. Tôi tin rằng sự lộn xộn thị giác và thêm sự rách rưới là không đáng. Tôi chưa bao giờ ở trong một tình huống mà tôi đã gán lại (hãy nhớ, điều này không làm cho các đối tượng bất biến, tất cả điều đó có nghĩa là bạn không thể gán lại một tham chiếu khác cho một biến) một biến bị lỗi. Quay trở lại.

Nhưng, tất nhiên, đó là tất cả các sở thích cá nhân ;-)

+5

+1 bởi vì tôi nghĩ rằng đó không chỉ là về phương pháp lỗi cơ thể và logic (mà tôi chưa bao giờ chạy chỉ vì các biến không phải là cuối cùng, FWIW), mà còn về các chữ ký phương thức có thể đọc được. Tôi không thể thấy cách tham số được khai báo là cuối cùng làm cho chữ ký dễ đọc hơn. Các loại đơn giản được truyền theo giá trị, do đó không thể sửa đổi được. Các kiểu phức tạp được truyền theo tham chiếu, nhưng tham chiếu được truyền theo giá trị, do đó không thể sửa đổi được. Cuối cùng chỉ là tiếng ồn cho người đọc chữ ký. – Matthias

0

Tại sao bạn muốn? Bạn đã viết phương thức này, vì vậy, bất kỳ ai sửa đổi phương thức đó luôn có thể xóa từ khóa cuối cùng khỏi qwerty và gán lại nó. Đối với chữ ký phương thức, cùng một lý do, mặc dù tôi không chắc chắn nó sẽ làm gì với các lớp con của lớp của bạn ... chúng có thể kế thừa tham số cuối cùng và thậm chí nếu chúng ghi đè phương thức, không thể hủy kết thúc x. Hãy thử nó và tìm hiểu xem nó sẽ làm việc.

Lợi ích thực sự duy nhất, sau đó, là nếu bạn làm cho thông số bất biến và nó mang đến cho trẻ em. Nếu không, bạn chỉ làm lộn xộn mã của bạn mà không có lý do đặc biệt tốt. Nếu nó không ép buộc bất kỳ ai tuân thủ các quy tắc của bạn, bạn tốt hơn là chỉ để lại một nhận xét tốt vì bạn không nên thay đổi tham số hoặc biến đó thay vì đưa ra điều chỉnh cuối cùng.

Sửa

Để đối phó với một lời nhận xét, tôi sẽ bổ sung thêm rằng nếu bạn đang nhìn thấy vấn đề hiệu suất, làm cho các biến và các thông số địa phương của bạn cuối cùng có thể cho phép trình biên dịch để tối ưu hóa mã của bạn tốt hơn. Tuy nhiên, từ quan điểm của bất biến mã của bạn, tôi đứng theo tuyên bố ban đầu của tôi.

+0

JRE hoặc trình biên dịch có thể thực hiện tối ưu hóa hơn nếu nó biết đối tượng được hoàn thành. – paxdiablo

+0

Chắc chắn, nhưng đó không phải là câu hỏi. Về việc làm cho mã của bạn không thay đổi được, nó không thực sự hoạt động. Tuy nhiên, bạn khá chính xác ở chỗ nó có thể nhanh hơn một chút so với không sử dụng nó, và cần được xem xét khi hiệu suất là một vấn đề. – Elie

+0

Như tôi đã nhận xét về câu trả lời của Pax, tôi khá chắc chắn rằng không có cải tiến hoàn hảo bằng cách tạo các biến cục bộ cuối cùng, vì JVM có thể đoán được điều đó cho bạn. – SCdF

5

Do tính chất khó hiểu (thỉnh thoảng) của hành vi "pass by reference" của Java, tôi nhất định đồng ý với việc hoàn thành tham số var.

Hoàn thành biến cục bộ có vẻ hơi quá mức IMO.

+0

Nhưng nếu bạn vượt qua Danh sách theo tham chiếu và đánh dấu nó là "cuối cùng" trong danh sách tham số, bạn vẫn có thể thêm hoặc xóa các phần tử và xem chúng được phản ánh trong người gọi - điều này chắc chắn gây nhầm lẫn hơn không đặt "cuối cùng" trên tham số trong trường hợp này? – mjaggard

+0

Không nhất thiết. Object mutability là một khái niệm khá mới mẻ IMO. Nơi tôi làm việc, chúng tôi hy vọng các nhà phát triển hiểu rằng một Object cuối cùng không có nghĩa là nó không thể thay đổi, chỉ là nó không thể được tham chiếu lại. – javamonkey79

6

Trong trường hợp các biến cục bộ, tôi có xu hướng tránh điều này. Nó gây ra sự lộn xộn thị giác, và nói chung là không cần thiết - một chức năng phải đủ ngắn hoặc tập trung vào một tác động duy nhất để cho phép bạn nhanh chóng thấy rằng bạn đang sửa đổi cái gì đó không nên.

Trong trường hợp số ma thuật, tôi sẽ đặt chúng dưới dạng trường riêng tư liên tục, thay vì trong mã.

Tôi chỉ sử dụng cuối cùng trong các trường hợp cần thiết (ví dụ: chuyển giá trị cho các lớp ẩn danh).

0

Tôi để Eclipse làm điều đó cho tôi khi chúng đang được sử dụng trong một lớp ẩn danh, tăng lên do tôi sử dụng API thu thập của Google.

0

Chúng tôi thực hiện tại đây cho các biến địa phương nếu chúng tôi cho rằng chúng sẽ không được gán lại hoặc không được gán lại.

Các tham số không phải là cuối cùng vì chúng tôi có một Checkstyle-Check kiểm tra các thông số gán lại. Tất nhiên không ai có thể muốn gán lại một biến tham số.

2

thức có ba lý do chính đáng: biến

  • dụ thiết lập bởi constructor chỉ trở nên bất biến
  • phương pháp không được ghi đè trở thành cuối cùng, sử dụng điều này với lý do thực sự, không theo mặc định
  • biến địa phương hoặc các tham số được sử dụng trong các lớp không đồng nhất bên trong một phương thức cần phải là cuối cùng

Giống như các phương pháp, biến cục bộ và thông số không cần phải khai báo cuối cùng. Như những người khác đã nói trước đây, điều này cắt mã trở nên ít có thể đọc được với rất ít efford cho tối ưu hóa trình biên dịch performace, đây không phải là lý do thực sự cho hầu hết các đoạn mã.

3

Có làm điều đó.

Đó là về khả năng đọc. Nó dễ dàng hơn để lý luận về các trạng thái có thể của chương trình khi bạn biết rằng các biến được gán một lần và chỉ một lần.

Phương án thay thế là bật cảnh báo IDE khi tham số được gán hoặc khi một biến (ngoài biến vòng lặp) được gán nhiều lần.

+0

hoàn thành sẽ không giữ lại một tham số hoặc biến trở thành biến đổi, nó không giống như const trong C++. Chuyển danh sách tới tham số cuối cùng, bạn vẫn có thể xóa nó. –

+0

Nó không phải về an toàn, đó là về khả năng đọc. Rõ ràng làm cho một tham số cuối cùng không có hiệu lực trên đó các quốc gia có thể ở phần cuối của phương pháp. –

+2

@Motlin: nó dành cho các đối tượng nguyên thủy và không thay đổi, bao gồm rất nhiều thông số trong kinh nghiệm của tôi. –

8

Làm một tham số thức đảm bảo rằng giá trị sử dụng ở bất kỳ vị trí trong phương pháp đề cập đến giá trị thông qua. Nếu không, bạn phải phân tích cú pháp tinh thần tất cả các mã trên một vị trí nhất định để biết giá trị tham số có tại điểm đó.

Do đó, không sử dụng thức làm cho mã của bạn dễ đọc hơn, và duy trì, tất cả bởi chính nó :)

biến địa phương cuối cùng phụ thuộc vào ý định, và ít quan trọng trong quan điểm của tôi. Phụ thuộc vào những gì diễn ra.

+0

Hãy cẩn thận rằng cuối cùng chỉ đánh dấu tham chiếu là không thay đổi. Các thuộc tính của đối tượng được tham chiếu có thể thay đổi bất cứ lúc nào. Vì vậy, bạn sẽ chỉ đạt được mức độ đảm bảo này nếu bạn làm cho toàn bộ đồ thị đối tượng không thay đổi được. Tuy nhiên, đây là một thực hành rất tốt, thậm chí còn khó mà đạt được trong java. – ordnungswidrig

+0

Nó đảm bảo rằng đối tượng được gọi là giống nhau. Các _contents_ của đối tượng nói có thể, tự nhiên, thay đổi. –

2

Mặc dù nó tạo ra một chút lộn xộn, nhưng đáng giá là đặt final. Ví dụ: nhật thực có thể tự động đặt final nếu bạn định cấu hình để làm như vậy.

2

Tạo biến cục bộ và tham số phương pháp final là điều cần thiết nếu bạn muốn chuyển các tham số đó vào các lớp ẩn danh - như bạn khởi tạo một chuỗi ẩn danh và muốn truy cập các tham số đó trong phần thân của phương thức run().

Ngoài ra, tôi không chắc chắn về các lợi ích hiệu suất mà hiệu suất tốt hơn thông qua tối ưu hóa trình biên dịch. Đó là tùy thuộc vào việc thực hiện trình biên dịch cụ thể cho dù nó muốn tối ưu hóa nó ...

Nó sẽ là tốt để biết về bất kỳ số liệu thống kê hiệu suất từ ​​việc sử dụng final ...