Tại sao từ khóa 'cuối cùng' được sử dụng rất ít trong ngành? [đóng cửa]


14

Vì tôi đã phát hiện ra sức mạnh của finaltừ khóa trong Java vài năm trước, nó đã giúp tôi làm cho mã của mình dễ đọc hơn rất nhiều, vì mọi người có thể dễ dàng thấy các biến chỉ đọc là gì. Nó cũng có thể cung cấp một chút thúc đẩy cho JIT và trong khi đó là một hạn chế rất lớn, nó không thể gây hại, đặc biệt là khi nhắm mục tiêu các thiết bị nhúng như nền tảng Android.

Nhưng hơn tất cả mọi thứ, nó góp phần làm cho một thiết kế mạnh mẽ hơn đối với các thay đổi và hướng dẫn các ủy viên khác khi họ cần sửa đổi cơ sở mã.

Tuy nhiên, trong khi duyệt một số mã nguồn JDK, tôi không bao giờ vấp phải từ khóa này, cho các lớp, phương thức hoặc biến. Điều tương tự áp dụng cho các hệ thống khác nhau tôi đã phải xem xét.

Có một lý do? Có phải là một mô hình thiết kế chung để cho mọi thứ có thể thay đổi từ bên trong?


trong tối ưu hóa vi mô, finalcác trường cài đặt có ngữ nghĩa tương tự như viết một volatiletrường và sau đó đọc chúng cần có ngữ nghĩa đọc dễ bay hơi, đây không phải là điều bạn muốn
ratchet freak

5
@ratchet: Tôi nghĩ rằng đây là nhiều hơn về việc làm cho các lớp của bạn trở nên bất biến, như trong lập trình chức năng.
Jonas

@Kwebble: Có lẽ quan trọng hơn, các trường hợp Chuỗi riêng lẻ là bất biến.
MatrixFrog

Câu trả lời:


9

Tôi nghi ngờ rằng lý do bạn không thấy là vì JDK được thiết kế để mở rộng (nó là cơ sở mà tất cả các chương trình của bạn được xây dựng trên đó). Sẽ không có gì lạ khi mã ứng dụng sử dụng nó. Tôi cũng sẽ không thấy nhiều mã trong thư viện (vì các thư viện cũng thường được thiết kế để mở rộng).

Hãy thử xem một số dự án nguồn mở tốt và tôi nghĩ bạn sẽ tìm thấy nhiều hơn.

Ngược lại với những gì bạn đang thấy trong Java, trong thế giới .NET, tôi đã nghe thấy đối số nhiều lần rằng các phân đoạn nên sealed(tương tự như áp dụng cuối cùng cho một lớp) theo mặc định và nhà phát triển nên bỏ niêm phong rõ ràng nó


2
Tôi thậm chí đã đề cập đến nhiều thành viên riêng tư hoặc được bảo vệ của một số lớp JDK rõ ràng là "sinh ra một lần, không bao giờ ghi đè" các biến đối tượng. Đặc biệt trong gói đu. Đó là những ứng cử viên hoàn hảo cho 'trận chung kết', vì việc thay thế bằng một con trỏ null sẽ dẫn đến lỗi khi sử dụng các thành phần.
Aurelien Ribon

1
@ Aurélien: Nếu điều đó làm bạn cảm thấy tốt hơn, nhiều lớp trong .NET Framework đã được niêm phong, phần lớn là sự cấu thành của một số người dùng muốn mở rộng các lớp khung với chức năng riêng của họ. Tuy nhiên, hạn chế này có thể được giảm bớt phần nào bằng cách sử dụng Phương thức mở rộng.
Robert Harvey

1
Tôi nghĩ rằng đây là về sự bất biến hơn là tránh mở rộng. Nói cách khác, việc sử dụng finaltrên các trường và không phải trên các phương thức. Các lớp bất biến cũng có thể được thiết kế để được mở rộng.
Jonas

15

Vấn đề với việc sử dụng finalđể truyền đạt rằng một cái gì đó chỉ đọc là nó chỉ thực sự hoạt động đối với các kiểu nguyên thủy như int, charv.v. Tất cả các đối tượng trong Java thực sự được gọi là sử dụng một con trỏ (loại). Kết quả là, khi bạn sử dụng finaltừ khóa trên một đối tượng, bạn chỉ nói rằng tham chiếu là chỉ đọc, bản thân đối tượng vẫn có thể thay đổi.

Nó có thể đã được sử dụng nhiều hơn nếu nó thực sự làm cho đối tượng chỉ đọc. Trong C ++, đó chính xác là những gì constnó làm và kết quả là nó là một từ khóa hữu ích và được sử dụng nhiều hơn nhiều.

Một nơi tôi sử dụng finaltừ khóa rất nhiều là với các tham số để tránh mọi sự nhầm lẫn được tạo bởi những thứ như thế này:

public void someMethod(FancyObject myObject) {
    myObject = new FancyObject();
    myObject.setProperty(7);
}
...
public static void main(final String[] args) {
    ...
    FancyObject myObject = new FancyObject();
    someOtherObject.someMethod(myObject);
    myObject.getProperty(); // Not 7!
}

Trong ví dụ này có vẻ rõ ràng tại sao điều này không hoạt động nhưng nếu someMethod(FancyObject)sự nhầm lẫn lớn và phức tạp có thể xảy ra. Tại sao không tránh nó?

Nó cũng là một phần của tiêu chuẩn mã hóa Mặt trời (hoặc Oracle bây giờ tôi đoán).


1
Nếu đã được sử dụng nhiều hơn finaltrong các lớp API Java, hầu hết các đối tượng đã không thay đổi như với nhiều thư viện Scala. Sau đó, làm cho các trường đối tượng finallàm cho lớp bất biến trong nhiều trường hợp. Thiết kế này đã được sử dụng trong BigDecimalString.
Jonas

@Jonas Tôi đã đọc câu hỏi để biết thêm về điểm 4 trong câu trả lời của schmmd vì anh ta đang nói "vì mọi người có thể dễ dàng nhìn thấy các biến chỉ đọc là gì" và trả lời tương ứng. Có lẽ tôi nên đưa giả định đó vào câu trả lời.
Gyan aka Gary Buyn

Hãy nhớ rằng có thời gian khi bạn muốn gán lại tham số. Ví dụ là cắt một tham số chuỗi.
Steve Kuo

@Steve Tôi thường tạo một biến mới để được chỉ định trong tình huống đó.
Gyan aka Gary Buyn

1
@Gary, đối với tôi là ít nhất, rất hiếm khi xảy ra lỗi / nhầm lẫn do gán lại một tham số. Tôi muốn thay đổi mã miễn phí và chấp nhận cơ hội từ xa để gán lại một tham số gây ra lỗi. Chỉ cần cẩn thận khi viết / sửa đổi mã.
Steve Kuo

4

Tôi đồng ý rằng finaltừ khóa cải thiện khả năng đọc. Nó làm cho nó dễ hiểu hơn nhiều khi một đối tượng là bất biến. Tuy nhiên, trong Java nó cực kỳ dài dòng, đặc biệt (theo ý kiến ​​của tôi) khi được sử dụng trên các tham số. Điều này không có nghĩa là mọi người không nên sử dụng nó vì nó dài dòng, mà là họ không sử dụng nó vì nó dài dòng.

Một số ngôn ngữ khác, chẳng hạn như scala, làm cho việc khai báo cuối cùng ( val ) dễ dàng hơn nhiều . Trong các ngôn ngữ này, khai báo cuối cùng có thể phổ biến hơn các biến.

Lưu ý rằng có nhiều cách sử dụng khác nhau của từ khóa cuối cùng. Bài viết của bạn chủ yếu bao gồm các mục 2 và 3.

  1. Các lớp cuối cùng (JLS 8.1.1.2)
  2. Trường cuối cùng (JLS 8.3.1.2)
  3. Phương pháp cuối cùng (JLS 8.4.3.3)
  4. Biến cuối cùng (JLS 4.12.4)

2

Vâng, để bắt đầu, các Công ước về Mã Java chính thức không ủng hộ cũng không cấm sử dụng cụ thể final. Đó là, các nhà phát triển JDK có thể tự do lựa chọn cách họ thích.

Tôi không thể đọc được suy nghĩ của họ nhưng với tôi, việc ưu tiên sử dụng finalhay không là vấn đề trọng tâm. Một vấn đề liệu tôi có đủ thời gian để tập trung hoàn toàn vào mã hay không .

  • Giả sử, trong một trong các dự án của tôi, chúng tôi có thể đủ khả năng chi tiêu trung bình một ngày cho khoảng 100 dòng mã. Trong dự án này, tôi đã có một nhận thức khác biệt về finalmột thứ rác rưởi chỉ che khuất những thứ đã được thể hiện rõ ràng trong mã. Có vẻ như các nhà phát triển JDK cũng thuộc loại đó.
     
    Mặt khác, nó hoàn toàn trái ngược trong một dự án khác, nơi chúng tôi đã dành trung bình một giờ cho 100 dòng mã. Ở đó, tôi thấy mình bắn finalnhư một khẩu súng mashine theo cách riêng của mình và trong mã khác - đơn giản vì đó là cách nhanh nhất để phát hiện ý định của kẻ đã viết nó trước tôi và, tương tự, cách nhanh nhất để truyền đạt ý định của tôi người sẽ làm việc với mã của tôi sau này.

Nó cũng có thể cung cấp một chút thúc đẩy cho JIT, và trong khi đó là một hạn chế rất lớn, nó không thể gây hại

Lý luận như trên là trơn trượt. Tối ưu hóa sớm có thể gây hại; Donald Knuth đã đi xa đến mức gọi nó là "gốc rễ của mọi tội lỗi" . Đừng để nó bẫy bạn. Viết mã câm .


2

Gần đây tôi phát hiện ra niềm vui của tính năng "Lưu hành động" trong IDE Eclipse. Tôi có thể buộc nó định dạng lại mã của mình, chèn @Overridecác chú thích bị thiếu và thực hiện một số nội dung tiện lợi như xóa dấu ngoặc đơn không cần thiết trong biểu thức hoặc finaltự động đặt từ khóa ở mọi nơi mỗi khi tôi nhấn ctrl + S. Tôi đã kích hoạt một số kích hoạt đó và, cậu bé, nó giúp ích rất nhiều!

Nó chỉ ra rằng nhiều trong số các kích hoạt đó hoạt động như một kiểm tra độ tỉnh táo nhanh chóng cho mã của tôi.

  • Tôi định ghi đè một phương thức nhưng chú thích không hiển thị khi tôi nhấn ctrl + s? - có lẽ tôi đã làm hỏng các loại tham số ở đâu đó!
  • Một số dấu ngoặc đơn đã bị xóa khỏi mã khi lưu? - có lẽ biểu thức logic đó quá khó để lập trình viên có thể giải quyết nhanh chóng. Nếu không, tại sao tôi lại thêm các dấu ngoặc đơn đó ở vị trí đầu tiên?
  • Tham số đó hoặc biến cục bộ không final. Liệu nó phải thay đổi giá trị của nó?

Nó chỉ ra rằng càng ít biến số thay đổi thì tôi càng ít gặp rắc rối trong thời gian gỡ lỗi. Đã bao nhiêu lần bạn theo dõi giá trị của một số biến chỉ để thấy rằng bằng cách nào đó thay đổi từ 5 đến 7? "Làm thế quái nào nó có thể được?!" bạn tự hỏi mình và dành vài giờ tiếp theo bước vào và ra vô số phương pháp để biết rằng bạn đã phạm sai lầm trong logic của mình. Và để khắc phục, bạn phải thêm một cờ nữa, một vài điều kiện và cẩn thận thay đổi một số giá trị ở đây và ở đó.

Ồ, tôi ghét gỡ lỗi! Mỗi lần tôi chạy trình gỡ lỗi, tôi cảm thấy như thời gian của mình sắp hết và tôi rất cần thời gian đó để biến ít nhất một số giấc mơ thời thơ ấu của tôi trở thành sự thật! Đến địa ngục với gỡ lỗi! finals có nghĩa là không có nhiều thay đổi giá trị bí ẩn. Hơnfinal s => ít phần mỏng manh hơn trong mã của tôi => ít lỗi hơn => nhiều thời gian hơn để làm công cụ tốt!

Đối với finalcác lớp học và phương pháp tôi không thực sự quan tâm. Tôi yêu đa hình. Đa hình có nghĩa là tái sử dụng có nghĩa là ít mã hơn có nghĩa là ít lỗi hơn. JVM thực hiện một công việc khá tốt với việc ảo hóa và phương thức nội tuyến vì vậy tôi không thấy giá trị nào trong việc tiêu diệt các khả năng sử dụng lại mã cho các lợi ích hiệu năng không rõ ràng.


Nhìn thấy tất cả những gì finaltrong mã lúc đầu hơi mất tập trung và cũng mất thời gian để làm quen. Một số đồng đội của tôi vẫn rất ngạc nhiên khi thấy rất nhiều finaltừ khóa. Tôi ước có một thiết lập trong IDE để tô màu cú pháp đặc biệt cho nó. Tôi sẽ rất vui khi chuyển nó sang một số màu xám (như chú thích) để chúng không bị phân tâm khi đọc mã. Eclipse hiện có một màu riêng cho returnvà tất cả các từ khóa khác nhưng không phải cho final.


1
Bạn có thể có thể xem xét việc xem xét các ngôn ngữ chức năng như OCaml hoặc F #. Các ngôn ngữ này có xu hướng yêu cầu gỡ lỗi ít hơn nhiều, một trong những lý do là mọi thứ chỉ được đọc theo mặc định. Nói một cách đơn giản, một khi được gán, một biến trong ngôn ngữ chức năng sẽ không thay đổi.
Abel

1
Vâng, tôi biết điều đó và thỉnh thoảng tôi thực sự viết một số mã ML - đó là nơi tôi lấy cảm hứng từ đó :) Không phải về ngôn ngữ mà bạn sử dụng mà là về các nguyên tắc và kỹ thuật bạn áp dụng. Mọi người có thể sử dụng tất cả các mệnh lệnh và doký hiệu :) Chắc chắn, Java không phải là ngôn ngữ tốt nhất để viết theo phong cách chức năng nhưng ít nhất nó cho phép bạn viết mã mạnh mẽ - và đó là điều tôi thực sự quan tâm.
Andrew Андрей Листочкин
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.