Tại sao tôi nên sử dụng từ khóa Lần cuối cùng trên một tham số phương thức trong Java?


381

Tôi không thể hiểu finaltừ khóa thực sự tiện dụng khi nó được sử dụng trên các tham số phương thức.

Nếu chúng ta loại trừ việc sử dụng các lớp ẩn danh, khả năng đọc và khai báo ý định thì nó dường như gần như vô giá trị đối với tôi.

Thực thi rằng một số dữ liệu không đổi không mạnh như nó có vẻ.

  • Nếu tham số là nguyên thủy thì nó sẽ không có hiệu lực do tham số được truyền cho phương thức dưới dạng giá trị và thay đổi nó sẽ không có hiệu lực ngoài phạm vi.

  • Nếu chúng ta truyền tham số theo tham chiếu, thì chính tham chiếu đó là biến cục bộ và nếu tham chiếu được thay đổi từ bên trong phương thức, điều đó sẽ không có bất kỳ ảnh hưởng nào từ bên ngoài phạm vi phương thức.

Hãy xem xét ví dụ kiểm tra đơn giản dưới đây. Thử nghiệm này vượt qua mặc dù phương thức đã thay đổi giá trị của tham chiếu được cung cấp cho nó, nhưng nó không có hiệu lực.

public void testNullify() {
    Collection<Integer> c  = new ArrayList<Integer>();      
    nullify(c);
    assertNotNull(c);       
    final Collection<Integer> c1 = c;
    assertTrue(c1.equals(c));
    change(c);
    assertTrue(c1.equals(c));
}

private void change(Collection<Integer> c) {
    c = new ArrayList<Integer>();
}

public void nullify(Collection<?> t) {
    t = null;
}

101
Một điểm nhanh về thuật ngữ - Java hoàn toàn không có tham chiếu qua. Nó đã vượt qua tham chiếu theo giá trị không giống nhau. Với việc vượt qua đúng ngữ nghĩa tham chiếu, kết quả mã của bạn sẽ khác.
Jon Skeet

6
Sự khác biệt giữa vượt qua tham chiếu và vượt qua tham chiếu theo giá trị là gì?
NobleUplift

Thật dễ dàng để mô tả sự khác biệt đó trong bối cảnh C (ít nhất là đối với tôi). Nếu tôi chuyển một con trỏ tới một phương thức như: <code> int foo (int bar) </ code>, thì con trỏ đó đang được truyền theo giá trị. Có nghĩa là nó được sao chép, vì vậy nếu tôi làm một cái gì đó bên trong phương thức đó như <code> free (bar); bar = malloc (...); </ code> sau đó tôi vừa làm một điều thực sự tồi tệ. Cuộc gọi miễn phí sẽ thực sự giải phóng khối bộ nhớ được chỉ vào (vì vậy bất kỳ con trỏ nào tôi chuyển vào hiện đang treo lủng lẳng). Tuy nhiên, <code> int foo (int & bar) </ bar> có nghĩa là mã đó hợp lệ và giá trị của con trỏ được truyền vào sẽ bị thay đổi.
jerslan

1
Người đầu tiên được cho là int foo(int* bar)và người cuối cùng int foo(int* &bar). Cái sau đang truyền một con trỏ theo tham chiếu, cái trước là truyền tham chiếu theo giá trị.
jerslan

2
@Martin, theo tôi, đó là một câu hỏi hay ; xem tiêu đề cho câu hỏi và nội dung bài đăng như một lời giải thích cho lý do tại sao câu hỏi được hỏi. Có thể tôi đang hiểu sai các quy tắc ở đây, nhưng đây chính xác là câu hỏi tôi muốn khi tìm kiếm "sử dụng các tham số cuối cùng trong các phương thức" .
Victor Zamanian

Câu trả lời:


239

Dừng phân công lại biến

Mặc dù những câu trả lời này rất thú vị về mặt trí tuệ, tôi đã không đọc câu trả lời đơn giản ngắn:

Sử dụng từ khóa cuối cùng khi bạn muốn trình biên dịch ngăn chặn một biến được gán lại cho một đối tượng khác.

Cho dù biến là biến tĩnh, biến thành viên, biến cục bộ hoặc biến đối số / biến tham số, hiệu ứng hoàn toàn giống nhau.

Thí dụ

Chúng ta hãy xem hiệu quả trong hành động.

Hãy xem xét phương thức đơn giản này, trong đó hai biến ( argx ) đều có thể được gán lại các đối tượng khác nhau.

// Example use of this method: 
//   this.doSomething( "tiger" );
void doSomething( String arg ) {
  String x = arg;   // Both variables now point to the same String object.
  x = "elephant";   // This variable now points to a different String object.
  arg = "giraffe";  // Ditto. Now neither variable points to the original passed String.
}

Đánh dấu biến cục bộ là cuối cùng . Điều này dẫn đến một lỗi biên dịch.

void doSomething( String arg ) {
  final String x = arg;  // Mark variable as 'final'.
  x = "elephant";  // Compiler error: The final local variable x cannot be assigned. 
  arg = "giraffe";  
}

Thay vào đó, hãy đánh dấu biến tham số là cuối cùng . Điều này cũng dẫn đến một lỗi biên dịch.

void doSomething( final String arg ) {  // Mark argument as 'final'.
  String x = arg;   
  x = "elephant"; 
  arg = "giraffe";  // Compiler error: The passed argument variable arg cannot be re-assigned to another object.
}

Đạo đức của câu chuyện:

Nếu bạn muốn đảm bảo một biến luôn trỏ đến cùng một đối tượng, hãy đánh dấu biến cuối cùng .

Không bao giờ gán lại đối số

Là một thực hành lập trình tốt (trong bất kỳ ngôn ngữ nào), bạn không bao giờ nên gán lại một biến tham số / đối số cho một đối tượng khác với đối tượng được truyền bởi phương thức gọi. Trong các ví dụ trên, người ta không bao giờ nên viết dòng này arg =. Vì con người mắc lỗi và lập trình viên là con người, chúng ta hãy yêu cầu trình biên dịch hỗ trợ chúng ta. Đánh dấu mọi biến tham số / tham số là 'cuối cùng' để trình biên dịch có thể tìm và gắn cờ mọi phép gán lại như vậy.

Khi nhìn lại

Như đã lưu ý trong các câu trả lời khác, mục tiêu thiết kế ban đầu của Java đưa ra là giúp các lập trình viên tránh các lỗi ngu ngốc như đọc qua phần cuối của mảng, Java nên được thiết kế để tự động thực thi tất cả các biến tham số / tham số là 'cuối cùng'. Nói cách khác, đối số không nên là biến . Nhưng tầm nhìn xa là tầm nhìn 20/20 và các nhà thiết kế Java đã có trong tay đầy đủ vào thời điểm đó.

Vì vậy, luôn luôn thêm finalvào tất cả các đối số?

Chúng ta có nên thêm finalvào từng tham số phương thức được khai báo không?

  • Về lý thuyết, có.
  • Trong thực tế, không.
    Only finalChỉ thêm khi mã của phương thức dài hoặc phức tạp, trong đó đối số có thể bị nhầm với biến cục bộ hoặc biến thành viên và có thể được gán lại.

Nếu bạn mua vào thực tế không bao giờ gán lại một đối số, bạn sẽ có xu hướng thêm một finalđối số. Nhưng điều này là tẻ nhạt và làm cho tuyên bố khó đọc hơn một chút.

Đối với mã đơn giản ngắn trong đó đối số rõ ràng là một đối số và không phải là biến cục bộ cũng không phải là biến thành viên, tôi không bận tâm thêm final. Nếu mã khá rõ ràng, không có cơ hội nào cho tôi cũng như bất kỳ lập trình viên nào khác đang bảo trì hoặc tái cấu trúc lại vô tình nhầm nhầm biến đối số là một thứ khác hơn là một đối số, thì đừng bận tâm. Trong công việc của riêng tôi, tôi finalchỉ thêm vào mã dài hơn hoặc nhiều hơn có liên quan trong đó một đối số có thể bị nhầm với biến cục bộ hoặc biến thành viên.

Một trường hợp khác được thêm vào cho sự hoàn chỉnh

public class MyClass {
    private int x;
    //getters and setters
}

void doSomething( final MyClass arg ) {  // Mark argument as 'final'.

   arg =  new MyClass();  // Compiler error: The passed argument variable arg  cannot be re-assigned to another object.

   arg.setX(20); // allowed
  // We can re-assign properties of argument which is marked as final
 }

23
"Là một thực hành lập trình tốt (trong bất kỳ ngôn ngữ nào), bạn không bao giờ nên gán lại biến tham số / biến đối số [..]" Xin lỗi tôi thực sự phải gọi bạn ra khỏi ngôn ngữ này. Việc gán lại các đối số là thông lệ tiêu chuẩn trong các ngôn ngữ như Javascript, trong đó số lượng đối số được truyền (hoặc thậm chí nếu có bất kỳ đối số nào được thông qua) không được quy định bởi chữ ký phương thức. Ví dụ: được cung cấp một chữ ký như: "function say (dir)" mọi người sẽ đảm bảo đối số 'thông điệp' được gán, như vậy: "dir = dir || 'Hello World!';". Các lập trình viên Javascript tốt nhất trên thế giới đang phá vỡ sự thực hành tốt của bạn. Chỉ cần đọc nguồn jQuery.
Stijn de Witt

37
@StijndeWitt Ví dụ của bạn cho thấy chính vấn đề gán lại biến đối số. Bạn mất thông tin mà không thu được gì : (a) Bạn đã mất giá trị ban đầu được thông qua, (b) Bạn đã mất ý định của phương thức gọi (Người gọi đã vượt qua 'Xin chào thế giới!' Hoặc chúng tôi đã mặc định). Cả a & b đều hữu ích để thử nghiệm, mã dài và khi giá trị được thay đổi thêm sau đó. Tôi đứng trước tuyên bố của mình: arg vars không bao giờ nên được chỉ định lại. Mã của bạn phải là : message = ( msg || 'Hello World"' ). Đơn giản là không có lý do để không sử dụng một var riêng biệt. Chi phí duy nhất là một vài byte bộ nhớ.
Basil Bourque

9
@Basil: Đó là nhiều mã hơn (tính bằng byte) và trong Javascript có tính. Nặng nề. Cũng như nhiều thứ, ý kiến ​​của nó dựa trên. Hoàn toàn có thể bỏ qua thực hành lập trình này và vẫn viết mã xuất sắc. Thực hành lập trình của một người không làm cho mọi người thực hành. Đứng bên cạnh tất cả những gì bạn muốn, tôi chọn cách viết khác đi. Điều đó làm cho tôi trở thành một lập trình viên xấu, hoặc mã của tôi mã xấu?
Stijn de Witt

14
Sử dụng message = ( msg || 'Hello World"' )rủi ro tôi sau này vô tình sử dụng msg. Khi hợp đồng mà tôi dự định là "hành vi không có / null / không xác định arg không thể phân biệt được với việc chuyển "Hello World"", đó là một cách lập trình tốt để cam kết thực hiện sớm chức năng này. [Điều này có thể đạt được mà không cần xác định lại bằng cách bắt đầu bằng if (!msg) return myfunc("Hello World");nhưng điều đó trở nên khó sử dụng với nhiều đối số.] .
Beni Cherniavsky-Paskin

6
@ BeniCéciavsky-Paskin rủi ro bạn mô tả chỉ là do sự giống nhau giữa messagemsg. Nhưng nếu anh ta gọi nó là một cái gì đó giống như processedMsghoặc một cái gì đó cung cấp bối cảnh bổ sung - khả năng sai lầm thấp hơn nhiều. Tập trung vào những gì anh ấy nói không phải là "làm thế nào" anh ấy nói nó. ;)
alfasin

230

Đôi khi thật tốt khi nói rõ ràng (để dễ đọc) rằng biến không thay đổi. Đây là một ví dụ đơn giản trong đó việc sử dụng finalcó thể cứu một số vấn đề đau đầu có thể xảy ra:

public void setTest(String test) {
    test = test;
}

Nếu bạn quên từ khóa 'this' trên setter, thì biến bạn muốn đặt sẽ không được đặt. Tuy nhiên, nếu bạn đã sử dụng finaltừ khóa trên tham số, thì lỗi sẽ bị bắt tại thời điểm biên dịch.


65
btw bạn sẽ thấy cảnh báo "Việc gán cho kiểm tra biến không có tác dụng" dù sao đi nữa
AvrDragon

12
@AvrDragon Nhưng, chúng tôi cũng có thể bỏ qua cảnh báo. Vì vậy, sẽ tốt hơn nếu có thứ gì đó ngăn chúng ta tiến xa hơn, như lỗi biên dịch, mà chúng ta sẽ nhận được bằng cách sử dụng từ khóa cuối cùng.
Sumit Desai

10
@AvrDragon Điều đó phụ thuộc vào môi trường phát triển. Bạn không nên dựa vào IDE để bắt những thứ như thế này cho bạn, trừ khi bạn muốn phát triển những thói quen xấu.
b1nary.atr0phy

25
@ b1naryatr0phy thực sự là một cảnh báo của trình biên dịch, không chỉ là mẹo IDE
AvrDragon

8
@SumitDesai "Nhưng, chúng tôi cũng có thể bỏ qua cảnh báo. Vì vậy, sẽ tốt hơn nếu có thứ gì đó ngăn chúng tôi tiến xa hơn, như lỗi biên dịch, mà chúng tôi sẽ nhận được bằng cách sử dụng từ khóa cuối cùng." Tôi nêu quan điểm của bạn nhưng đây là một tuyên bố rất mạnh mẽ mà tôi nghĩ rằng nhiều nhà phát triển Java sẽ không đồng ý. Cảnh báo trình biên dịch là có lý do và một nhà phát triển có thẩm quyền không cần phải có lỗi để 'buộc' họ xem xét ý nghĩa của nó.
Stuart Rossiter

127

Có, không bao gồm các lớp ẩn danh, khả năng đọc và khai báo ý định gần như vô giá trị. Là ba điều vô giá trị mặc dù?

Cá nhân tôi có xu hướng không sử dụng finalcho các biến và tham số cục bộ trừ khi tôi sử dụng biến trong lớp bên trong ẩn danh, nhưng tôi chắc chắn có thể thấy quan điểm của những người muốn làm rõ rằng giá trị tham số sẽ không thay đổi (thậm chí nếu đối tượng nó đề cập đến thay đổi nội dung của nó). Đối với những người tìm thấy thêm khả năng đọc, tôi nghĩ đó là một điều hoàn toàn hợp lý để làm.

Quan điểm của bạn sẽ quan trọng hơn nếu bất cứ ai thực sự tuyên bố rằng nó đã giữ dữ liệu không đổi theo cách mà nó không - nhưng tôi không thể nhớ đã thấy bất kỳ khiếu nại nào như vậy. Bạn có gợi ý rằng có một nhóm các nhà phát triển đáng kể cho rằng nó finalcó hiệu quả hơn thực tế không?

EDIT: Tôi thực sự nên tóm tắt tất cả những điều này với một tài liệu tham khảo Monty Python; câu hỏi có vẻ hơi giống với câu hỏi "Người La Mã đã làm gì cho chúng ta?"


17
Nhưng để diễn giải Krusty với người Đan Mạch của mình, họ đã làm gì cho chúng ta MỚI NHẤT ? =)
James Schek

Yuval. Thật là buồn cười! Tôi đoán hòa bình có thể xảy ra ngay cả khi nó được thi hành bằng thanh kiếm!
gonzobrains

1
Câu hỏi đặt ra có vẻ tương tự hơn để hỏi, "Điều gì đã không người La Mã làm cho chúng ta?", Bởi vì nó là chi tiết của một bài phê bình về những gì từ khóa thức không làm.
NobleUplift

"Bạn có gợi ý rằng có một nhóm các nhà phát triển đáng kể cho rằng trận chung kết có nhiều tác dụng hơn thực tế không?" Đối với tôi đó vấn đề chính: Tôi cực kỳ nghi ngờ một tỷ lệ đáng kể các nhà phát triển sử dụng nó nghĩ rằng nó thực thi tính bất biến của các mục gọi của người gọi khi không có. Tất nhiên, sau đó người ta bị lôi kéo vào cuộc tranh luận về việc liệu các tiêu chuẩn mã hóa có nên 'bảo vệ chống lại những hiểu lầm về khái niệm (mà một nhà phát triển' có thẩm quyền 'nên biết) hay không (và điều này sau đó hướng đến một ý kiến ​​ngoài phạm vi SO câu hỏi -type)!
Stuart Rossiter

1
@SarthakMittal: Giá trị sẽ không được sao chép trừ khi bạn thực sự sử dụng nó, nếu đó là điều bạn đang thắc mắc.
Jon Skeet

76

Hãy để tôi giải thích một chút về trường hợp bạn phải sử dụng cuối cùng, mà Jon đã đề cập:

Nếu bạn tạo một lớp bên trong ẩn danh trong phương thức của mình và sử dụng một biến cục bộ (chẳng hạn như một tham số phương thức) bên trong lớp đó, thì trình biên dịch buộc bạn phải thực hiện tham số cuối cùng:

public Iterator<Integer> createIntegerIterator(final int from, final int to)
{
    return new Iterator<Integer>(){
        int index = from;
        public Integer next()
        {
            return index++;
        }
        public boolean hasNext()
        {
            return index <= to;
        }
        // remove method omitted
    };
}

Ở đây from, tocác tham số cần phải là cuối cùng để chúng có thể được sử dụng bên trong lớp ẩn danh.

Lý do cho yêu cầu đó là: Các biến cục bộ sống trên ngăn xếp, do đó chúng chỉ tồn tại trong khi phương thức được thực thi. Tuy nhiên, cá thể lớp ẩn danh được trả về từ phương thức, vì vậy nó có thể tồn tại lâu hơn nhiều. Bạn không thể bảo toàn ngăn xếp, vì nó cần thiết cho các cuộc gọi phương thức tiếp theo.

Vì vậy, những gì Java làm thay vào đó là đặt các bản sao của các biến cục bộ đó thành các biến thể hiện ẩn vào lớp ẩn danh (bạn có thể thấy chúng nếu bạn kiểm tra mã byte). Nhưng nếu chúng không phải là cuối cùng, người ta có thể mong đợi lớp ẩn danh và phương thức nhìn thấy những thay đổi mà lớp kia tạo ra cho biến. Để duy trì ảo tưởng rằng chỉ có một biến chứ không phải hai bản sao, nó phải là bản cuối cùng.


1
Bạn đã mất tôi từ "Nhưng nếu chúng không phải là cuối cùng ...." Bạn có thể cố gắng viết lại nó không, Có lẽ tôi đã không có đủ cofee.
hhafez

1
Bạn có một biến cục bộ từ - câu hỏi là điều gì xảy ra nếu bạn sử dụng thể hiện lớp anon bên trong phương thức và nó thay đổi các giá trị từ - mọi người sẽ mong đợi sự thay đổi được hiển thị trong phương thức, vì họ chỉ thấy một biến. Để tránh sự nhầm lẫn này, nó phải là cuối cùng.
Michael Borgwardt

Nó không tạo ra một bản sao, nó chỉ đơn giản là một tham chiếu đến bất kỳ đối tượng nào được tham chiếu.
vickirk

1
@vickirk: chắc chắn rằng nó tạo một bản sao - của tài liệu tham khảo, trong trường hợp các loại tham chiếu.
Michael Borgwardt

Btw giả sử chúng ta không có các lớp ẩn danh tham chiếu các biến đó, bạn có biết nếu có bất kỳ sự khác biệt nào giữa finaltham số hàm và tham số hàm không cuối cùng trong mắt HotSpot không?
Pacerier

25

Tôi sử dụng cuối cùng tất cả các thời gian trên các tham số.

Nó có thêm nhiều không? Không hẳn vậy.

Tôi sẽ tắt nó chứ? Không.

Lý do: Tôi tìm thấy 3 lỗi trong đó mọi người đã viết mã cẩu thả và không thể đặt biến thành viên trong bộ truy cập. Tất cả các lỗi đã được chứng minh là khó tìm.

Tôi muốn thấy điều này làm mặc định trong phiên bản Java trong tương lai. Việc vượt qua bởi giá trị / điều tham khảo đi lên rất nhiều lập trình viên cơ sở.

Một điều nữa .. các phương thức của tôi có xu hướng có số lượng tham số thấp nên văn bản bổ sung trên khai báo phương thức không phải là vấn đề.


4
Tôi cũng sắp đề xuất điều này, cuối cùng đó là mặc định trong các phiên bản trong tương lai và bạn phải chỉ định từ khóa "có thể thay đổi" hoặc tốt hơn được hình thành. Đây là một bài viết hay về điều này: lpar.ath0.com/2008/08/26/java-annoyance-final-parameter
Jeff Axelrod

Đã lâu rồi, nhưng bạn có thể đưa ra một ví dụ về lỗi bạn gặp phải không?
user949300

Xem bình chọn nhiều nhất để trả lời. Đó là một ví dụ thực sự tốt khi biến thành viên không được đặt và thay vào đó tham số bị đột biến.
Fortyrunner

18

Sử dụng cuối cùng trong một tham số phương thức không liên quan gì đến những gì xảy ra với đối số ở phía người gọi. Nó chỉ có nghĩa là đánh dấu nó là không thay đổi bên trong phương thức đó. Khi tôi cố gắng áp dụng một phong cách lập trình chức năng hơn, tôi thấy giá trị của nó.


2
Chính xác, nó không phải là một phần của giao diện chức năng, chỉ thực hiện. Thật khó hiểu khi java cho phép (nhưng dirsregards) final trên các tham số trong các giao diện / khai báo phương thức trừu tượng.
Beni Cherniavsky-Paskin

8

Cá nhân tôi không sử dụng cuối cùng cho các tham số phương thức, bởi vì nó thêm quá nhiều lộn xộn vào danh sách tham số. Tôi thích thực thi rằng các tham số phương thức không được thay đổi thông qua một cái gì đó như Checkstyle.

Đối với các biến cục bộ mà tôi sử dụng cuối cùng bất cứ khi nào có thể, tôi thậm chí còn để Eclipse tự động làm điều đó trong thiết lập của mình cho các dự án cá nhân.

Tôi chắc chắn sẽ thích một cái gì đó mạnh mẽ hơn như C / C ++ const.


Không chắc chắn các tài liệu tham khảo IDE và công cụ có thể áp dụng cho bài đăng OP hoặc chủ đề. Cụ thể là "cuối cùng" là một kiểm tra thời gian biên dịch rằng tham chiếu không bị thay đổi / kết hợp. Hơn nữa, để thực sự thực thi những điều như vậy, hãy xem câu trả lời về việc không bảo vệ các thành viên nhí của các tài liệu tham khảo cuối cùng. Khi xây dựng API chẳng hạn, sử dụng IDE hoặc công cụ sẽ không giúp các bên ngoài sử dụng / mở rộng mã đó.
Darrell Teague

4

Vì Java vượt qua các bản sao của các đối số, tôi cảm thấy mức độ liên quan của finalnó khá hạn chế. Tôi đoán thói quen này xuất phát từ thời đại C ++ nơi bạn có thể cấm nội dung tham chiếu bị thay đổi bằng cách thực hiện a const char const *. Tôi cảm thấy loại công cụ này khiến bạn tin rằng nhà phát triển vốn đã ngu ngốc như f *** và cần được bảo vệ chống lại mọi nhân vật anh ta nhập. Trong tất cả sự khiêm tốn tôi có thể nói, tôi viết rất ít lỗi mặc dù tôi bỏ qua final(trừ khi tôi không muốn ai đó ghi đè lên các phương thức và lớp của tôi). Có lẽ tôi chỉ là một dev trường học cũ.


1

Tôi không bao giờ sử dụng cuối cùng trong một danh sách tham số, nó chỉ thêm lộn xộn như những người trả lời trước đã nói. Ngoài ra trong Eclipse, bạn có thể đặt gán tham số để tạo ra lỗi, vì vậy việc sử dụng cuối cùng trong danh sách tham số có vẻ khá dư thừa đối với tôi. Thật thú vị khi tôi kích hoạt cài đặt Eclipse để gán tham số tạo ra lỗi trên nó đã bắt được mã này (đây chỉ là cách tôi nhớ luồng, không phải mã thực tế.): -

private String getString(String A, int i, String B, String C)
{
    if (i > 0)
        A += B;

    if (i > 100)
        A += C;

    return A;
}

Chơi người ủng hộ của quỷ, chính xác thì có gì sai khi làm điều này?


Hãy cẩn thận để phân biệt IDE với JVM thời gian chạy. Bất cứ điều gì IDE làm đều không liên quan khi mã byte được biên dịch chạy trên máy chủ trừ khi IDE thêm mã để bảo vệ chống lại sự biến đổi thành viên, chẳng hạn như lỗ hổng trong mã khi dự định rằng một biến không nên được gán lại nhưng nhầm lẫn là - vì vậy mục đích của từ khóa cuối cùng.
Darrell Teague

1

Câu trả lời ngắn: finalgiúp một chút xíu nhưng ... sử dụng lập trình phòng thủ ở phía máy khách.

Thật vậy, vấn đề với finallà nó chỉ thực thi tham chiếu là không thay đổi, vui vẻ cho phép các thành viên đối tượng được tham chiếu bị đột biến, không biết đến người gọi. Do đó, cách tốt nhất trong vấn đề này là lập trình phòng thủ ở phía người gọi, tạo ra các trường hợp bất biến sâu sắc hoặc các bản sao sâu của các đối tượng có nguy cơ bị chặn bởi các API vô đạo đức.


2
"Vấn đề cuối cùng là nó chỉ thực thi tham chiếu là không thay đổi" - Không đúng, chính Java đã ngăn chặn điều đó. Một biến được truyền vào một phương thức không thể thay đổi tham chiếu của nó bằng phương thức đó.
Madbreaks

Vui lòng nghiên cứu trước khi đăng ... stackoverflow.com/questions/40480/ Kẻ
Darrell Teague

Nói một cách đơn giản, nếu đúng là không thể thay đổi các tham chiếu đến các tham chiếu, thì sẽ không có cuộc thảo luận nào về sao chép phòng thủ, bất biến, không cần từ khóa cuối cùng, v.v.
Darrell Teague

Hoặc bạn đang hiểu lầm tôi, hoặc bạn nhầm. Nếu tôi chuyển một tham chiếu đối tượng cho một phương thức và phương thức đó gán lại cho nó, thì tham chiếu ban đầu vẫn còn nguyên cho (tôi) người gọi khi phương thức hoàn thành việc thực thi. Java hoàn toàn vượt qua giá trị. Và bạn đang tự phụ một cách trơ trẽn để khẳng định tôi đã không nghiên cứu.
Madbreaks

Downvote vì op hỏi tại sao sử dụng cuối cùng, và bạn chỉ đưa ra một lý do không chính xác.
Madbreaks

0

Một lý do bổ sung để thêm cuối cùng vào khai báo tham số là nó giúp xác định các biến cần được đổi tên như là một phần của tái cấu trúc "Phương thức trích xuất". Tôi đã thấy rằng việc thêm cuối cùng vào từng tham số trước khi bắt đầu tái cấu trúc phương thức lớn nhanh chóng cho tôi biết nếu có bất kỳ vấn đề nào tôi cần giải quyết trước khi tiếp tục.

Tuy nhiên, tôi thường loại bỏ chúng là không cần thiết vào cuối quá trình tái cấu trúc.


-1

Theo dõi bởi những gì bài viết của Michel. Tôi tự làm cho mình một ví dụ khác để giải thích nó. Tôi hy vọng nó có thể giúp đỡ.

public static void main(String[] args){
    MyParam myParam = thisIsWhy(new MyObj());
    myParam.setArgNewName();

    System.out.println(myParam.showObjName());
}

public static MyParam thisIsWhy(final MyObj obj){
    MyParam myParam = new MyParam() {
        @Override
        public void setArgNewName() {
            obj.name = "afterSet";
        }

        @Override
        public String showObjName(){
            return obj.name;
        }
    };

    return myParam;
}

public static class MyObj{
    String name = "beforeSet";
    public MyObj() {
    }
}

public abstract static class MyParam{
    public abstract void setArgNewName();
    public abstract String showObjName();
}

Từ đoạn code trên, trong phương pháp thisIsWhy () , chúng tôi thực sự đã không gán các [luận myObj obj] đến một tài liệu tham khảo thực trong MyParam. Thay vào đó, chúng tôi chỉ sử dụng [đối số MyObj obj] trong phương thức bên trong MyParam.

Nhưng sau khi chúng ta kết thúc phương thức thisIsWhy () , đối số (đối tượng) MyObj có còn tồn tại không?

Có vẻ như nó nên, bởi vì chúng ta có thể thấy trong main, chúng ta vẫn gọi phương thức showObjName () và nó cần phải đạt obj . MyParam vẫn sẽ sử dụng / đạt đến đối số phương thức ngay cả khi phương thức đã được trả về!

Cách Java thực sự đạt được điều này là tạo ra một bản sao cũng là một tham chiếu ẩn của đối số MyObj obj bên trong đối tượng MyParam (nhưng nó không phải là một trường chính thức trong MyParam để chúng ta không thể nhìn thấy nó)

Khi chúng ta gọi "showObjName", nó sẽ sử dụng tham chiếu đó để lấy giá trị tương ứng.

Nhưng nếu chúng ta không đặt đối số cuối cùng, dẫn đến một tình huống, chúng ta có thể gán lại một bộ nhớ (đối tượng) mới cho đối số MyObj obj .

Về mặt kỹ thuật không có xung đột nào cả! Nếu chúng ta được phép làm điều đó, dưới đây sẽ là tình huống:

  1. Bây giờ chúng ta có một [MyObj obj] ẩn đến một [Bộ nhớ A trong heap] hiện đang sống trong đối tượng MyParam.
  2. Chúng tôi cũng có một [MyObj obj] khác, đó là điểm đối số của [Bộ nhớ B trong heap] hiện đang sống trong phương thức ThisIsWhy.

Không đụng độ, nhưng "XÁC NHẬN !!" Bởi vì tất cả họ đều sử dụng cùng một "tên tham chiếu" là "obj" .

Để tránh điều này, hãy đặt nó là "cuối cùng" để tránh lập trình viên thực hiện mã "dễ mắc lỗi".

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.