2010-10-12 27 views
59

Tôi rất ngạc nhiên khi thấy rằng các đoạn mã Java sau biên dịch và chạy:Làm thế nào để "cuối cùng int i" làm việc bên trong của một vòng lặp Java?

for(final int i : listOfNumbers) { 
    System.out.println(i); 
} 

nơi listOfNumbers là một mảng các số nguyên.

Tôi nghĩ các khai báo cuối cùng chỉ được gán một lần. Trình biên dịch có tạo đối tượng Integer và thay đổi đối tượng tham chiếu không?

Trả lời

66

Hãy tưởng tượng viết tắt mà trông rất giống này:

for (Iterator<Integer> iter = listOfNumbers.iterator(); iter.hasNext();) 
{ 
    final int i = iter.next(); 
    { 
     System.out.println(i); 
    } 
} 
14

Xem @TravisG cho một lời giải thích của các quy tắc Phạm vi liên quan. Lý do duy nhất tại sao bạn sẽ sử dụng một biến vòng lặp cuối cùng như thế này là nếu bạn cần phải đóng nó trong một lớp bên trong vô danh.

import java.util.Arrays; 
import java.util.List; 

public class FinalLoop { 
    public static void main(String[] args) { 
     List<Integer> list = Arrays.asList(new Integer[] { 0,1,2,3,4 }); 
     Runnable[] runs = new Runnable[list.size()]; 

     for (final int i : list) { 
      runs[i] = new Runnable() { 
        public void run() { 
         System.out.printf("Printing number %d\n", i); 
        } 
       }; 
     } 

     for (Runnable run : runs) { 
      run.run(); 
     } 
    } 
} 

Biến vòng lặp không nằm trong phạm vi của Runnable khi nó được thực thi, chỉ khi nó được khởi tạo. Nếu không có từ khóa final, biến vòng lặp sẽ không hiển thị với Runnable khi nó chạy cuối cùng. Ngay cả khi nó được, nó sẽ là cùng một giá trị cho tất cả các Runnables.

Btw, khoảng 10 năm trước, bạn có thể đã thấy một cải thiện tốc độ rất nhỏ trong việc sử dụng cuối cùng trên một biến địa phương (trong một số trường hợp hiếm hoi); đó không phải là trường hợp trong một thời gian dài. Bây giờ lý do duy nhất để sử dụng cuối cùng là cho phép bạn sử dụng một đóng cửa từ vựng như thế này.

Trong câu trả lời cho @mafutrct:

Khi bạn viết mã, bạn làm như vậy cho hai đối tượng. Đầu tiên là máy tính mà, miễn là nó là cú pháp chính xác, sẽ được hạnh phúc với bất kỳ lựa chọn bạn thực hiện. Thứ hai là dành cho người đọc trong tương lai (thường là chính bạn), ở đây mục tiêu là truyền đạt 'ý định' của mã, mà không che khuất 'chức năng' của mã. Các tính năng ngôn ngữ nên được sử dụng thành ngữ với ít giải thích nhất có thể để giảm sự mơ hồ.

Trong trường hợp của biến vòng lặp, việc sử dụng cuối cùng có thể được sử dụng để giao tiếp một trong hai điều: phân bổ đơn lẻ; hoặc, đóng cửa. Một quét tầm thường của vòng lặp sẽ cho bạn biết nếu biến vòng lặp được gán lại; tuy nhiên, do sự xen kẽ của hai đường dẫn thực hiện khi tạo ra một đóng cửa, nó có thể dễ dàng bỏ lỡ việc đóng cửa nhằm nắm bắt biến. Trừ khi, tất nhiên, bạn chỉ bao giờ sử dụng cuối cùng để chỉ ra ý định để nắm bắt, tại thời điểm đó nó trở nên rõ ràng cho người đọc đó là những gì đang xảy ra.

+7

Hoặc để ghi lại ý định không làm cho biến này có thể gán lại. –

+2

Không! Nếu bạn làm điều này, bây giờ bạn có ý nghĩa ngữ nghĩa cho cùng một cú pháp - một cú pháp có sử dụng thực; khác là dư thừa trừ khi bạn phương pháp là xa quá xa. Chỉ sử dụng cuối cùng để cho biết bạn có ý định đóng tham số, _never_ để chỉ ra phép gán đơn lẻ. – Recurse

+0

@Recurse Điều đó nghe có vẻ hợp lý. Bạn có thể giải thích lý do đằng sau nó mặc dù? Tôi không chắc tôi hiểu lý do – mafu

Các vấn đề liên quan