2015-06-19 30 views
31

Với đoạn mã sau:Tại sao try..finally chặn không đăng ký ngoại lệ ban đầu là bị chặn?

try { 
    throw new RuntimeException ("main"); 
} 
finally { 
    throw new RuntimeException ("finally"); 
} 

tôi nhận được kết quả này:

Exception in thread "main" java.lang.RuntimeException: finally 
     at test.main(test.java:12) 

Tuy nhiên, với việc bổ sung các trường hợp ngoại lệ ức chế trong Java 7, nó sẽ không được hợp lý cho các ngôn ngữ để đăng ký ban đầu ngoại lệ "chính" bị chặn khi không có mã số finally không có ngoại lệ? Hiện nay tôi phải tự thi đua này:

try { 
    throw new RuntimeException ("main"); 
} 
catch (RuntimeException exception) { 
    try { 
     throw new RuntimeException ("finally"); 
    } 
    catch (RuntimeException exception2) { 
     exception2.addSuppressed (exception); 
     throw exception2; 
    } 
} 

nhận hữu ích hơn (để hiểu những gì đang xảy ra) Kết quả:

Exception in thread "main" java.lang.RuntimeException: finally 
     at test.main(test.java:13) 
     Suppressed: java.lang.RuntimeException: main 
       at test.main(test.java:9) 

EDIT: Để làm rõ những gì tôi đang tự hỏi. Phiên bản Java hiện tại là 8, các ngoại lệ bị chặn không phải là một tính năng hoàn toàn mới. Nhưng try..finally vẫn không kết hợp chúng. Có điều gì ngăn cản điều này xảy ra không?

+1

Ngoại lệ bị loại bỏ không phải là trường hợp được ném vào 'cuối cùng'? – StenSoft

+1

@StenSoft: Ngay cả khi có lẽ hợp lý hơn, điều đó sẽ phá vỡ tính tương thích ngược hoàn toàn. – doublep

+4

Tôi không hiểu các phiếu bầu gần gũi, phải trung thực. Đây là một câu hỏi hoàn toàn hợp pháp. – fge

Trả lời

7

Bởi vì try-with-resources là cú pháp đường và trình biên dịch Java không mở rộng các khối cố gắng cuối cùng cố gắng theo cùng một cách.

Hãy nhìn vào đoạn mã sau:

try(FileInputStream fstream = new FileInputStream("test")) { 
    fstream.read(); 
} 

Khi biên soạn và sau đó dịch ngược (dùng IntelliJ IDEA) nó trông như thế này:

FileInputStream fstream = new FileInputStream("test"); 
Throwable var2 = null; 

try { 
    fstream.read(); 
} catch (Throwable var19) { 
    var2 = var19; 
    throw var19; 
} finally { 
    if(fstream != null) { 
     if(var2 != null) { 
      try { 
       fstream.close(); 
      } catch (Throwable var17) { 
       var2.addSuppressed(var17); 
      } 
     } else { 
      fstream.close(); 
     } 
    } 
} 

Trong khi mã này:

FileInputStream fstream = new FileInputStream("test"); 
try { 
    fstream.read(); 
} finally { 
    fstream.close(); 
} 

Trông giống hệt nhau khi được biên dịch và biên dịch.

Bây giờ, trường hợp chắc chắn có thể được thực hiện là tất cả các khối finally sẽ được mở rộng theo cách tương tự như được thực hiện ở trên, nhưng vì một số lý do bị bỏ qua hoặc quyết định.

Tôi đề nghị bạn open a feature request vì điều này vì tôi nghĩ đó là một tính năng hợp lý.

+4

Tôi đọc câu hỏi là "* Tại sao trình biên dịch Java không mở rộng các khối cố gắng cuối cùng cố gắng theo cùng một cách *", mặc dù tôi nghĩ câu trả lời này vẫn bổ sung một cái gì đó hữu ích. – Marvin

+0

Câu hỏi đặt ra là "Có điều gì ngăn cản điều này xảy ra không?" * Và lý do nó không xảy ra là vì nó không được thực hiện. Đối với * tại sao * không được triển khai, chúng tôi chỉ có thể đoán hoặc hỏi Oracle (có thể bằng cách mở yêu cầu tính năng). – Raniz

+0

Tôi nghĩ rằng trong cuộc sống thực, một ngoại lệ trong một cuối cùng thường xảy ra vì đóng tài nguyên ngoại lệ. Đây là lý do tại sao, tôi nghĩ rằng, một giao diện AutoCloseable phải được sử dụng cho exemple: lớp Cleanup thực hiện AutoCloseable { @Override public void close() ném ngoại lệ { cleanup(); }} ... thử (Cleanup dọn dẹp = new Cleanup()) {// một số ngoại lệ được ném đây } – aurya

5

Đây không phải là câu trả lời có thẩm quyền nhưng có vẻ như thay đổi đó sẽ phá vỡ tính tương thích hoặc try-with-resourcestry-finally sẽ không nhất quán với nhau.

Ngữ nghĩa trong try-with-resources là ngoại lệ được ném từ khối try được truyền đi, với ngoại lệ được ném trong finally được đăng ký là bị chặn. Điều này có ý nghĩa từ một quan điểm thực tế, bạn muốn bắt ngoại lệ "thực sự" của bạn và sau đó có thể cũng đối phó với các nguồn lực không đóng cửa nếu bạn muốn.

Nhưng trong try-finally nó là ngoại lệ ném vào finally được tuyên truyền (trong khi một từ try là "nuốt") và do đó chúng tôi có một lựa chọn ba giải pháp xấu:

  1. Đảo ngược logic của try-finally để căn chỉnh nó với try-with-resources và mã hủy hoại (hoặc đúng hơn là lỗi) tương thích với tất cả các mã trước đó.
  2. Tiếp tục tuyên truyền finally ngoại lệ với try ngoại lệ được đăng ký là bị chặn, làm cho hai cấu trúc không phù hợp với nhau.
  3. Cứ để mọi thứ như vậy.

Chủ quan tôi thấy 3 kém hơn 1 hoặc 2, mặc dù khá dễ dàng để tranh luận khác. Tuy nhiên, tôi nghi ngờ rằng đây là một tình thế tiến thoái lưỡng nan mà các nhà phát triển ngôn ngữ phải đối mặt và họ tình cờ lựa chọn phương án 3.

+0

Yeah, tôi cho rằng 2 là tốt hơn so với 3 (1 rõ ràng là ra khỏi câu hỏi). Lý do là từ quan điểm của tôi ngoại lệ là trong 90% trường hợp cho các vấn đề gỡ lỗi, chỉ có 10% là để đánh bắt. Có thông tin gỡ lỗi có giá trị hơn, ngay cả khi không phù hợp với một số trường hợp khác, theo ý kiến ​​của tôi, tốt hơn nhiều so với việc có ít hơn. – doublep

+0

@doublep Phần đó là chủ quan, tôi đồng ý và có thể có những lập luận rất tốt cho cả ba thực sự. Tôi chắc chắn không có ý nói 3 chắc chắn là lựa chọn đúng và mọi thứ khác đều sai. Tôi chỉ muốn chỉ ra một sự khác biệt quan trọng giữa hai cấu trúc, mà có thể ảnh hưởng đến quyết định. Ví dụ: – biziclop

+2

... ví dụ bạn có thể nói rằng 1 là lựa chọn tốt nhất vì 'cố gắng cuối cùng 'bị hỏng và sửa lỗi (và đưa nó phù hợp với' try-with-resources' quan trọng hơn việc duy trì tính tương thích ngược cho vì lợi ích của mã có lẽ là lỗi, – biziclop

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