2016-12-25 14 views
20

Nhìn qua Joshua Bloch Java hiệu quả - Second Edition ', tôi stumbled khi các mã sau vào trang 152:Đang tích cực ném AssertionError trong thực hành tốt Java?

double apply(double x, double y) { 
    switch(this) { 
     case PLUS: return x + y; 
     case MINUS: return x - y; 
     case TIMES: return x * y; 
     case DIVIDE: return x/y; 
    } 
    throw new AssertionError("Unknown op: " + this); 
} 

Bây giờ những gì confuses me là, rằng AssertionError đang tích cực ném. Đó có phải là thực hành tốt không? Để xác nhận sự hiểu biết của tôi được sử dụng để không can thiệp vào mã như vậy khi lập trình java được khởi động mà không có xác nhận được bật và do đó các câu lệnh khẳng định không được thực thi, hành vi không thay đổi. Tôi sẽ khá bối rối, nếu tôi nhận được AssertionException khi tôi chạy một chương trình mà thậm chí không bật xác nhận.

Mặc dù tôi hiểu rằng trường hợp mẫu có thể xảy ra khá thường xuyên, bạn phân tích một vài tùy chọn khác nhau và nếu không có tùy chọn nào khác, bạn nên ném ngoại lệ.

Vì vậy, thực hành tốt là ném một số AssertionException vào đây, hoặc tốt hơn là nên ném một cái khác? Nếu vậy, cái nào sẽ phù hợp nhất? Có lẽ IllegalArgumentException?


Chỉnh sửa để làm rõ: Câu hỏi của tôi không phải là về việc liệu chúng ta nên ném một Error ở đây, nhưng nếu chúng ta muốn ném một Exception hoặc một Error, cái nào nó nên được? Và thực tiễn tốt là chủ động ném AssertionError s? Tài liệu nói rằng Được cho là đã xác nhận rằng một xác nhận đã thất bại, vì vậy tôi có cảm giác rằng chúng ta không nên chủ động ném nó. Đúng không?


Second Chỉnh sửa: Xóa câu hỏi: Liệu nó tốt thực hành để chủ động ném một AssertionError, hoặc là nên tránh, mặc dù nó có thể? (Tôi đoán đọc tài liệu là sau)

+1

Nó khá dựa vào ý kiến. Về phần tôi, tôi ném 'AssertionError'. Tôi nghĩ lý do chính để không kiểm tra xác nhận trong sản xuất là chi phí kiểm tra xác nhận, và trong ví dụ trong câu hỏi tôi không nghĩ có một xác nhận. Tôi ở bên cạnh Bloch. –

+1

Đó chắc chắn là Lỗi, không phải là Ngoại lệ. Không có kịch bản hợp lý nơi người ta nên cố gắng nắm bắt lỗi và tiếp tục thực hiện. –

+0

Vì vậy, để trả lời câu hỏi tiêu đề từ những gì tôi hiểu từ các cuộc thảo luận ở đây: Có, nó là ok để chủ động ném một 'AssertionError' trong programm của bạn. Và nếu bạn làm như vậy, các xác nhận không cần phải được bật. –

Trả lời

17

Tôi đồng ý với ông Bloch ở đây - các lựa chọn thay thế (IllegalArgumentException, IllegalStateExceptionUnsupportedOperationException) không truyền đạt đúng mức độ nghiêm trọng của vấn đề và người gọi có thể cố gắng nắm bắt và xử lý trường hợp này một cách sai lầm. Trên thực tế, nếu dòng này bao giờ đạt đến chương trình được đề cập là bị hỏng và điều duy nhất cần làm là thoát. Điểm này ở đây là enum có một tập hợp các giá trị hữu hạn, do đó không thể đạt được dòng throw - nó sẽ chỉ xảy ra nếu định nghĩa của enum đã thay đổi mà không cần sửa phương thức thể hiện này. Ném một RuntimeException cho thấy người gọi đã thực hiện một sai lầm, khi trong thực tế, phương pháp (và enum) chính nó bị hỏng. Nâng cao rõ ràng một AssertionError chỉ ra một cách chính xác các bất biến mà phương pháp này mong đợi đã bị vi phạm.

Ổi có bài viết hữu ích chia nhỏ when to raise different types of exceptions. Họ viết:

A conventional assertion is a check that should only fail if the class itself (that contains the check) is broken in some way. (In some cases this can extend to the package.) These can take various forms including postconditions, class invariants, and internal preconditions (on non-public methods).

An impossible-condition check is one that cannot possibly fail unless surrounding code is later modified, or our deepest assumptions about platform behavior are grossly violated. These should be unnecessary but are often forced because the compiler can't recognize that a statement is unreachable, or because we know something about the control flow that the compiler cannot deduce.

Trang này cho biết cách xử lý các trường hợp này là AssertionError. Các nhận xét trong lớp học Verify của họ cũng cung cấp một số thông tin chi tiết hữu ích về việc chọn ngoại lệ. Trong trường hợp AssertionError có vẻ quá mạnh, hãy tăng VerifyException có thể là một sự thỏa hiệp tốt.

Đối với các câu hỏi cụ thể của Error hoặc RuntimeException, nó không thực sự quan trọng (cả hai đều không được kiểm soát và do đó có khả năng sẽ tiếp tục hành trình cuộc gọi stack mà không bị bắt), nhưng người gọi có nhiều khả năng để cố gắng phục hồi từ một RuntimeException . Việc bẻ khóa ứng dụng trong một trường hợp như thế này là một tính năng , bởi vì nếu không chúng tôi sẽ tiếp tục chạy một ứng dụng (vào thời điểm này) không chính xác. Nó chắc chắn ít có khả năng người gọi sẽ nắm bắt và xử lý AssertionError (hoặc Error hoặc Throwable), nhưng tất nhiên người gọi có thể làm bất cứ điều gì họ muốn.

+0

Vì vậy, bạn sẽ nói rằng nó là ok để ném một 'AssertionError' tích cực trong một số trường hợp? Hoặc chỉ nên thực hiện bởi trình biên dịch khi câu lệnh 'assert' được sử dụng? –

+0

Chắc chắn; 'AssertionError' cho biết các bất biến của chương trình đã bị vi phạm. – dimo414

5

Theo ý kiến ​​của tôi, AssertionError sẽ không chính xác để sử dụng tại đây.

From the docs, một AssertionError mở rộng lớp cơ sở Error

An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch.

lỗi nên gây tử vong, trong khi tôi mong chờ chương trình của bạn để xử lý này, và hiển thị cho người dùng một thông điệp cảnh báo về hoạt động chưa biết.

Nếu có gì ở đây, tôi mong đợi một số UnsupportedOperationException sẽ được ném và xử lý ở nơi khác trong ngăn xếp cuộc gọi.

Thrown to indicate that the requested operation is not supported.

xem xét, trường hợp không trong một máy tính, nhưng bất kỳ dòng mã có sử dụng enums:

Nếu một nhà phát triển đã thêm một giá trị mới cho một enum hiện có, tôi không mong chờ chức năng mà làm cho sử dụng enum hiện tại này để gọi một lỗi, chỉ vì giá trị mới không được hỗ trợ.

+0

Chắc chắn nó là gây tranh cãi cho dù chúng ta nên hiển thị một tin nhắn hoặc ném một lỗi. Nhưng chúng ta hãy giả sử chúng ta muốn ném một lỗi: Cái nào sẽ là cái đúng? Nó có thể thực sự là 'AssertionError'? Docu cũng nói _Được hiển thị để cho biết rằng xác nhận đã thất bại_. Nhưng ở đây không có xác nhận nào cả - vì vậy tôi có cảm giác rằng điều này là trái với tài liệu. Đúng? –

+0

Xem chỉnh sửa của tôi. Tôi sẽ không nhất thiết muốn sử dụng một khẳng định ở đây cả. Các xác nhận nên được sử dụng để tăng lỗi, khi một cái gì đó được gọi là nói chung là đúng, thì không. –

+0

Nó không sai khi sử dụng assertionerror trong trường hợp này vì nó khẳng định rằng trường hợp này sẽ không bao giờ đạt được. – GurV

1

Tôi nghĩ cả AssertionError hoặc IllegalAE đều không tốt ở đây. Lỗi xác nhận không tốt như được chỉ ra trong câu trả lời của Matt. Và các đối số không sai ở đây, những đối số đó chỉ được truyền cho một phương thức sai hoạt động this. Vì vậy, IAE có thể không tốt. Tất nhiên đây là câu hỏi và câu trả lời dựa trên ý kiến.

Ngoài ra, tôi không chắc chắn cho phép xác nhận là bắt buộc đối với việc ném AssertionError hoặc một assertionError có nghĩa là các xác nhận đã được bật.

0

Như tôi đã hiểu, phương pháp của bạn là phương pháp của đối tượng enum. Trong hầu hết các trường hợp khi ai đó thêm giá trị enum mới, anh ta cũng nên sửa đổi phương thức "áp dụng". Bạn nên ném UnsupportedOperationException trong trường hợp này.

4

lỗi Về, các Java Tutorial trạng thái:

The second kind of exception is the error. These are exceptional conditions that are external to the application, and that the application usually cannot anticipate or recover from.

Ngoài ra, Programming With Assertions dẫn trạng thái:

Do not use assertions for argument checking in public methods.

Vì vậy, tôi nghĩ rằng ngoại lệ là đúng cách để kiểm tra cho các loại hình trường hợp.

Tôi khuyên bạn nên sử dụng new UnsupportedOperationException("Operator " + name() + " is not supported."); vì nó mô tả tốt hơn vấn đề theo ý kiến ​​của tôi (tức là nhà phát triển đã thêm giá trị enum nhưng quên triển khai yêu cầu).

Tuy nhiên tôi nghĩ rằng trường hợp mẫu này nên sử dụng một mô hình AbstractEnum thiết kế thay vì một công tắc:

PLUS { 
    double apply(double x, double y) { 
     return x + y; 
    } 
}, 
MINUS { 
    double apply(double x, double y) { 
     return x - y; 
    } 
}, 
TIMES { 
    double apply(double x, double y) { 
     return x * y; 
    } 
}, 
DIVIDE { 
    double apply(double x, double y) { 
     return x/y; 
    } 
}; 

abstract double apply(double x, double y); 

Đó là ít bị lỗi từ mã này sẽ không biên dịch cho đến khi tất cả các trường hợp thực hiện apply.

+0

"* Không sử dụng các xác nhận để kiểm tra đối số trong các phương thức công cộng. *" Ví dụ của Bloch không kiểm tra đối số của phương thức công khai - nó khẳng định rằng chính lớp * được xác định chính xác. Tôi khuyên bạn nên đọc phần trích dẫn của Java hiệu quả; anh ta tiếp tục khám phá mô hình mà bạn mô tả, và giải thích tại sao sử dụng enum đôi khi là thích hợp hơn. – dimo414

2

Tôi muốn

double apply(double x, double y) { 
    switch(this) { 
     case PLUS: return x + y; 
     case MINUS: return x - y; 
     case TIMES: return x * y; 
     default: assert this==DIVIDE: return x/y; 
    } 
} 
  1. Chúng ta không nên ném AssertionError vì nó chỉ nên dành cho khẳng định thực tế.
  2. Khác với các xác nhận và một số khối catch không được có một mã duy nhất không thể thực hiện được.

Nhưng tôi nhất muốn https://stackoverflow.com/a/41324246/348975

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