Mục đích sử dụng của IllegalStateException là gì?


76

Điều này được đưa ra trong một cuộc thảo luận với một đồng nghiệp ngày hôm nay.

Javadocs cho Java IllegalStateExceptionnói rằng nó:

Báo hiệu rằng một phương pháp đã được gọi vào một thời điểm bất hợp pháp hoặc không thích hợp. Nói cách khác, môi trường Java hoặc ứng dụng Java không ở trạng thái thích hợp cho hoạt động được yêu cầu.

Và Java hiệu quả nói (Mục 60, trang 248):

Một ngoại lệ thường được sử dụng lại khác là IllegalStateException. Đây thường là ngoại lệ để ném nếu lời gọi là bất hợp pháp vì trạng thái của đối tượng nhận. Ví dụ, đây sẽ là ngoại lệ để ném nếu người gọi cố gắng sử dụng một số đối tượng trước khi nó được khởi tạo đúng cách.

Có vẻ như có một chút khác biệt ở đây. Câu thứ hai của javadocs làm cho nó giống như ngoại lệ có thể mô tả một điều kiện rất rộng về trạng thái thực thi Java, nhưng mô tả trong Java hiệu quả làm cho nó giống như nó được sử dụng cho các điều kiện liên quan cụ thể đến trạng thái trạng thái của đối tượng có phương thức đã được gọi.

Các cách sử dụng mà tôi đã thấy trong JDK (ví dụ: bộ sưu tập, Matcher) và trong Guava dường như chắc chắn thuộc loại mà Java hiệu quả nói về ("Đối tượng này ở trạng thái không thể gọi phương thức này"). Điều này cũng có vẻ phù hợp với IllegalStateExceptionanh chị em của ' IllegalArgumentException.

Có bất kỳ IllegalStateExceptioncách sử dụng hợp pháp nào trong JDK liên quan đến "môi trường Java" hoặc "ứng dụng Java" không? Hoặc có bất kỳ hướng dẫn thực hành tốt nhất nào ủng hộ việc sử dụng nó cho trạng thái thực thi rộng hơn không? Nếu không, tại sao các javadocs lại được viết như vậy? ;)


4
Trên một có thể không liên quan, lưu ý, tôi nhận thấy thẻ [bất hợp pháp] của StackOverflow cho biết In Java, an exception that occurs when using multiple threads, whereby one thread modifies an object in a way that makes it incompatible with the use of that object in the second thread, thus putting the object into an illegal state.. Huh? Trường hợp nào này đến từ đâu?
Andrew McNamee

3
"Ứng dụng Java" là ứng dụng bạn viết và bạn có thể sử dụng IllegalStateExceptionở đó (trực tiếp hoặc vì bạn đang sử dụng Guava chẳng hạn). Sự khác biệt ở đâu?
Frank Pavageau

4
Thẻ wiki trông không có thật, tôi đã gửi một bản chỉnh sửa bằng cách mượn từ câu hỏi này một cách tự do; bạn sẽ thấy phiên bản mới sau khi nó đã được đánh giá ngang hàng.
Meriton

Câu trả lời:


43

Đây là một cách sử dụng đặc biệt hợp pháp của ngoại lệ này trong JDK (xem: URLConnection.setIfModifiedSince(long)trong số hơn 300 cách sử dụng khác của nó:

public void setIfModifiedSince(long ifmodifiedsince) {
    if (connected)
        throw new IllegalStateException("Already connected");
    ifModifiedSince = ifmodifiedsince;
}

Tôi nghĩ rằng ví dụ là khá rõ ràng. Nếu đối tượng ở trạng thái cụ thể (" Đã kết nối "), một số thao tác sẽ không được gọi. Trong trường hợp này khi kết nối được thiết lập, một số thuộc tính không thể được thiết lập.

Ngoại lệ này đặc biệt hữu ích khi lớp của bạn có một số trạng thái (máy trạng thái?) Thay đổi theo thời gian, làm cho một số phương thức không liên quan hoặc không thể thực hiện được. Suy nghĩ về một Carlớp học mà có start(), stop()fuel()phương pháp. Mặc dù gọi start()hai lần, lần lượt, có lẽ không có gì sai, nhưng đổ xăng cho một chiếc xe đã khởi động chắc chắn là một ý kiến ​​tồi. Cụ thể là - xe đang ở trong tình trạng không ổn định.

Có thể cho rằng API tốt sẽ không cho phép chúng tôi gọi các phương thức ở trạng thái sai để các vấn đề như vậy được phát hiện tại thời điểm biên dịch, không phải trong thời gian chạy. Trong ví dụ cụ thể này, việc kết nối với một URL sẽ trả về một đối tượng khác với một tập hợp con các phương thức, tất cả đều hợp lệ sau khi kết nối.


1
Tôi đồng ý với câu trả lời của bạn. Nhưng điều này không giải thích tại sao Effectiva Java và Ổi sử dụng ngoại lệ này như mộtIllegalArgumentException
Zarathustra

4

Đây là một ví dụ trong JDK. Có một gói riêng lớp gọi là java.lang.Shutdown. Nếu hệ thống đang tắt và bạn cố gắng thêm một hook mới, nó sẽ ném IllegalStateException. Người ta có thể lập luận rằng điều này đáp ứng các tiêu chí của hướng dẫn "javadoc" - vì nó là môi trường Java ở trạng thái sai.

class Shutdown {    
...

   /* Add a new shutdown hook.  Checks the shutdown state and the hook itself,
    * but does not do any security checks.
    */
    static void add(int slot, Runnable hook) {
        synchronized (lock) {
            if (state > RUNNING)
                throw new IllegalStateException("Shutdown in progress");

            if (hooks[slot] != null)
                throw new InternalError("Shutdown hook at slot " + slot + " already registered");

            hooks[slot] = hook;
        }
    }

Tuy nhiên, nó cũng minh họa rằng thực sự không có sự phân biệt giữa hướng dẫn "javadoc" và hướng dẫn "Java hiệu quả". Do cách Shutdown được thực hiện, việc shutdown-ness của JVM được lưu trữ trong một trường gọi là trạng thái. Do đó, nó cũng đáp ứng hướng dẫn "Java hiệu quả" về thời điểm sử dụng IllegalStateException, vì trường "trạng thái" là một phần của trạng thái của đối tượng nhận. Vì đối tượng nhận (Shutdown) ở trạng thái sai, nó ném IllegalStateException.

Theo tôi, hai mô tả về thời điểm sử dụng IllegalStateException là nhất quán. Mô tả Java hiệu quả thực tế hơn một chút, vậy thôi. Đối với hầu hết chúng ta, phần quan trọng nhất của toàn bộ môi trường Java là lớp mà chúng ta đang viết ngay bây giờ, vì vậy đó là điều mà tác giả đang tập trung vào.


1
Ah, tôi thích ví dụ này! Cú InternalErrorném ngay bên dưới ISE cũng cho thấy sự hữu ích của việc phân biệt "Bạn đã gây rối" và "Tôi đã làm rối". Ngoài ra, nó cho thấy trạng thái tĩnh (bạn có thể nói trạng thái "Ứng dụng Java" hoặc "Môi trường Java") vẫn được coi là trạng thái như thế nào đối với ISE.
Andrew McNamee

@AndrewMcNamee Tôi nghĩ điều đó AssertionErrorsẽ phù hợp hơn cho những trường hợp tôi biết rằng mình đã làm sai. Nó dường như InternalErrorchỉ vì nó nằm trong mã JDK chứ không phải mã ứng dụng - một sự khác biệt đáng ngờ.
Ramon

Đúng, tôi chắc chắn đồng ý về AssertionError; nó dường như chuyển tải "Tôi đã gây rối" trái ngược với "Bạn đã nhầm lẫn" (mà ISE ngụ ý). Tôi sử dụng nó rất nhiều trong defaulttrường hợp cho switches. IMO InternalErrorvẫn phục vụ một mục đích hữu ích; người đọc dấu vết ngăn xếp biết rằng họ có thể cho rằng "Chà, JDK đã lộn xộn", nhưng các tác giả JDK có thể đã bỏ qua.
Andrew McNamee

2

Tôi đoán nếu bạn thấy cách sử dụng của IllegalStateExceptiontôi, tôi sẽ nói thứ hai nếu thích hợp hơn. Ngoại lệ này được sử dụng trong nhiều gói

  • java.net
  • java.nio
  • java.util
  • java.util.concurrrent, v.v.

Để chỉ định một ví dụ ArrayBlockingQueue.add ném ngoại lệ này nếu hàng đợi đã đầy. Now full là trạng thái của đối tượng và nó đang được gọi vào thời điểm không thích hợp hoặc bất hợp pháp

Tôi đoán cả hai đều có nghĩa giống nhau nhưng khác biệt về từ ngữ.


1

Với một thư viện, nó sẽ ném một IllegalStateExceptionhoặc IllegalArgumentExceptionbất cứ khi nào nó phát hiện ra lỗi do mã người dùng, trong khi thư viện sẽ ném AssertionErrorbất cứ khi nào nó phát hiện ra lỗi do việc triển khai của chính thư viện.

Ví dụ, trong các bài kiểm tra của thư viện, bạn có thể mong đợi thư viện ném một lệnh IllegalStateExceptionkhi thứ tự của các lệnh gọi phương thức bị sai. Nhưng bạn sẽ không bao giờ mong đợi thư viện ném một AssertionError.


0

Không có 'sự khác biệt' ở đây. Không có gì trong từ ngữ của Bloch loại trừ những gì nó nói trong JLS. Bloch chỉ đơn giản nói rằng nếu bạn có trường hợp A, hãy loại bỏ ngoại lệ này. Ông không nói rằng ngoại lệ này được / nên được ném chỉ trong tình trạng này. JLS nói rằng ngoại lệ này được ném ra nếu A, B hoặc C.


Tôi cho rằng bạn có thể nói như vậy. Nhưng mặt khác, nếu nó có hoàn cảnh sử dụng dự định khác với hoàn cảnh anh ta đưa ra, thì điều đó có thể không có ý nghĩa vì nó sẽ ít cụ thể hơn. Nói cách khác, nếu ISE dự định được sử dụng trong các trường hợp khác với trường hợp hiện tại "Bạn đã gọi phương pháp này cho tôi, nhưng tôi không ở trạng thái có thể làm điều đó", thì nó có thể không nhiều thông tin. Nhưng phải thừa nhận là, tôi nghĩ rằng tôi là lông tách ở đây;)
Andrew McNamee

@AndrewMcNamee Tôi đã nói điều đó. Tôi không biết phần còn lại của bình luận của bạn có ý nghĩa gì, nhưng bạn đang cố tạo ra sự khác biệt mà không tồn tại. Bạn đang phạm phải một sai lầm hợp lý.
user207421

0

Tôi gặp phải điều này với:

try {
    MessageDigest digest = MessageDigest.getInstance("SHA-1");
    ...
} catch (NoSuchAlgorithmException e) {
    throw new AssertionError(e);
}

Tôi nghĩ rằng sẽ không thực tế nếu tôi ném nó IllegalStateExceptionở đây thay AssertionExceptionvì nó nằm trong danh mục "môi trường Java".

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.