Bắt một ngoại lệ và suy nghĩ lại, nhưng đó không phải là một ngoại lệ


10

Tôi tình cờ thấy mã trông giống như thế này:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        System.out.println("Error: " + ex);
        throw ex;
    }
}

void doSomething() {
    throw new RuntimeException();
}

Mã này làm tôi ngạc nhiên vì có vẻ như run()-method có khả năng ném Exception, vì nó bắt Exceptionvà sau đó lấy lại, nhưng phương thức này không được tuyên bố là ném Exceptionvà dường như không cần phải như vậy. Mã này biên dịch tốt (ít nhất là trong Java 11).

Kỳ vọng của tôi sẽ là tôi sẽ phải tuyên bố throws Exceptiontrong run()-method.

Thông tin bổ sung

Theo cách tương tự, nếu doSomethingđược tuyên bố ném IOExceptionthì chỉ IOExceptioncần khai báo theo phương run()pháp -method, ngay cả khi Exceptionbị bắt và rút lại.

void run() throws IOException {
    try {
        doSomething();
    } catch (Exception ex) {
        System.out.println("Error: " + ex);
        throw ex;
    }
}

void doSomething() throws IOException {
    // ... whatever code you may want ...
}

Câu hỏi

Java thường thích sự rõ ràng, lý do đằng sau hành vi này là gì? Có phải nó luôn như thế này? Điều gì trong Đặc tả ngôn ngữ Java cho phép run()phương thức không cần khai báo throws Exceptiontrong đoạn mã ở trên? (Nếu tôi thêm nó, IntelliJ cảnh báo tôi rằng Exceptionkhông bao giờ được ném).


3
Hấp dẫn. Trình biên dịch nào bạn đang sử dụng? Nếu đó là trình biên dịch IDE, thì hãy kiểm tra javac- Tôi đã gặp phải trường hợp trình biên dịch Eclipse nhẹ nhàng hơn.
M. Prokhorov

2
Tôi có thể tái tạo hành vi này trên openjdk-8. Đáng chú ý là biên dịch với -source 1.6cờ làm tăng lỗi biên dịch như mong đợi. Biên soạn với nguồn tương thích 7 không không gây lỗi biên dịch
Vogel612

1
có vẻ như trình biên dịch thông minh hơn kể từ Java 7 và thực hiện nhiều kiểm tra hơn về ngoại lệ thực tế có thể bị ném.
michalk

2
Câu hỏi này không phải là một bản sao và câu trả lời có thể được tìm thấy trong liên kết tôi cung cấpIn detail, in Java SE 7 and later, when you declare one or more exception types in a catch clause, and rethrow the exception handled by this catch block, the compiler verifies that the type of the rethrown exception meets the following conditions : 1. 1. The try block is able to throw it. 2. There are no other preceding catch blocks that can handle it. 3. It is a subtype or supertype of one of the catch clause's exception parameters.
michalk

2
Bản sao được đánh dấu hiện tại chắc chắn có liên quan, nhưng không cung cấp IMO câu trả lời đủ chi tiết. Có một liên kết đến JLS trong các bình luận cho câu trả lời ở đó, bên cạnh đó không có thông tin.
Simon Forsberg

Câu trả lời:


0

Tôi không quét qua JLSnhư bạn đã hỏi trong câu hỏi của bạn, vì vậy hãy lấy câu trả lời này bằng một hạt muối. Tôi muốn làm cho nó một bình luận, nhưng nó sẽ quá lớn.


Đôi khi tôi thấy buồn cười, làm thế nào javaclà khá "thông minh" trong một số trường hợp (như trong trường hợp của bạn), nhưng để lại rất nhiều điều khác sẽ được xử lý sau đó JIT. Trong trường hợp này, chỉ có trình biên dịch "có thể nói" rằng chỉ một cái RuntimeExceptionsẽ bị bắt. Điều này là hiển nhiên, đó là điều duy nhất bạn ném vào doSomething. Nếu bạn thay đổi một chút mã của bạn thành:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        Exception ex2 = new Exception();
        System.out.println("Error: " + ex);
        throw ex2;
    }
}

bạn sẽ thấy một hành vi khác, bởi vì bây giờ javaccó thể nói rằng có một cái mới Exceptionmà bạn đang ném, không liên quan đến cái bạn bắt được.

Nhưng mọi thứ còn xa lý tưởng, bạn có thể "lừa" trình biên dịch một lần nữa thông qua:

void run() {
    try {
        doSomething();
    } catch (Exception ex) {
        Exception ex2 = new Exception();
        ex2 = ex;
        System.out.println("Error: " + ex);
        throw ex2;
    }
}

IMO, vì ex2 = ex;nó không nên thất bại một lần nữa, nhưng nó không.

Chỉ trong trường hợp này được biên dịch với javac 13+33


Tôi đã đọc trong một số liên kết rằng ai đó cung cấp rằng nếu bạn gán lại ngoại lệ bị bắt trong khối bắt, thì trình biên dịch không thể thông minh. Tôi giả sử một cái gì đó tương tự áp dụng trong trường hợp này. Trình biên dịch biết rằng ex2ngoại lệ sẽ được ném, ban đầu nó được tạo như một Exceptionnhưng sau đó được gán lại ex, và do đó trình biên dịch không thể thông minh.
Simon Forsberg

@SimonForsberg ai đó có niềm đam mê JLScó thể đến và cung cấp các trích dẫn cần thiết để chứng minh điều này; tiếc là tôi không có chúng
Eugene

Đối với bản ghi, khi tôi thay đổi khối bắt để chứa sự xác định lại ngoại lệ bị bắt với chính nó ( ex = ex;), phép heuristic không còn được áp dụng. Hành vi này dường như được áp dụng cho tất cả các cấp nguồn từ 7 đến 11 và có thể là 13
Vogel612

Có một cái nhìn tại này câu hỏi mà cũng là một bản sao. Điều này và bản sao của bản sao có thể giải thích nó và cũng liên kết đến JLS.
michalk
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.