Hãy xem xét tình huống mà bạn có mã được cung cấp:
public void delete() throws IOException, SQLException { // Non-Compliant
/* ... */
}
Điều nguy hiểm ở đây là mã mà bạn viết để gọi delete()
sẽ giống như:
try {
foo.delete()
} catch (Exception e) {
/* ... */
}
Điều này cũng tệ. Và nó sẽ được bắt với một quy tắc khác là cờ bắt lớp Exception cơ sở.
Điều quan trọng là không viết mã khiến bạn muốn viết mã xấu ở nơi khác.
Quy tắc mà bạn đang gặp phải là một quy tắc khá phổ biến. Checkstyle có nó trong các quy tắc thiết kế của nó:
Ném
Hạn chế ném các câu lệnh vào một số lượng được chỉ định (1 theo mặc định).
Đặt vấn đề: Ngoại lệ là một phần của giao diện của phương thức. Việc tuyên bố một phương pháp để ném quá nhiều ngoại lệ bắt nguồn khác nhau làm cho việc xử lý ngoại lệ trở nên khó khăn và dẫn đến các thực tiễn lập trình kém như viết mã như bắt (Exception ex). Kiểm tra này buộc các nhà phát triển đặt ngoại lệ vào một hệ thống phân cấp sao cho trong trường hợp đơn giản nhất, chỉ có một loại ngoại lệ cần được kiểm tra bởi người gọi nhưng bất kỳ lớp con nào cũng có thể bị bắt nếu cần thiết.
Điều này mô tả chính xác vấn đề và vấn đề là gì và tại sao bạn không nên làm điều đó. Đó là một tiêu chuẩn được chấp nhận tốt mà nhiều công cụ phân tích tĩnh sẽ xác định và gắn cờ.
Và trong khi bạn có thể làm điều đó theo thiết kế ngôn ngữ, và có thể đôi khi đó là điều đúng đắn, đó là điều bạn nên xem và ngay lập tức "ừ, tại sao tôi lại làm điều này?" Có thể chấp nhận mã nội bộ khi mọi người đều bị kỷ luật đủ để không bao giờ catch (Exception e) {}
, nhưng thường xuyên hơn là tôi không thấy mọi người cắt góc đặc biệt là trong các tình huống nội bộ.
Đừng làm cho những người sử dụng lớp của bạn muốn viết mã xấu.
Tôi nên chỉ ra rằng tầm quan trọng của điều này đã giảm đi với Java SE 7 và sau đó bởi vì một câu lệnh bắt duy nhất có thể bắt được nhiều ngoại lệ ( Bắt nhiều loại ngoại lệ và lấy lại ngoại lệ với Kiểm tra loại cải tiến từ Oracle).
Với Java 6 trở về trước, bạn sẽ có mã giống như:
public void delete() throws IOException, SQLException {
/* ... */
}
và
try {
foo.delete()
} catch (IOException ex) {
logger.log(ex);
throw ex;
} catch (SQLException ex) {
logger.log(ex);
throw ex;
}
hoặc là
try {
foo.delete()
} catch (Exception ex) {
logger.log(ex);
throw ex;
}
Cả hai tùy chọn này với Java 6 đều lý tưởng. Cách tiếp cận đầu tiên vi phạm DRY . Nhiều khối làm cùng một việc, lặp đi lặp lại - một lần cho mỗi ngoại lệ. Bạn muốn đăng nhập ngoại lệ và suy nghĩ lại? Đồng ý. Các dòng mã giống nhau cho mỗi ngoại lệ.
Tùy chọn thứ hai là tồi tệ hơn vì nhiều lý do. Đầu tiên, nó có nghĩa là bạn đang nắm bắt tất cả các ngoại lệ. Con trỏ Null bị bắt ở đó (và nó không nên). Hơn nữa, bạn đang rethrowing một Exception
có nghĩa là phương pháp chữ ký sẽ được deleteSomething() throws Exception
mà chỉ làm cho một mớ hỗn độn thêm lên stack như những người sử dụng mã của bạn bây giờ được buộc vào catch(Exception e)
.
Với Java 7, đây không phải là quan trọng bởi vì bạn thay vì có thể làm:
catch (IOException|SQLException ex) {
logger.log(ex);
throw ex;
}
Hơn nữa, các loại kiểm tra nếu một không bắt các loại trường hợp ngoại lệ được ném:
public void rethrowException(String exceptionName)
throws IOException, SQLException {
try {
foo.delete();
} catch (Exception e) {
throw e;
}
}
Trình kiểm tra loại sẽ nhận ra rằng e
có thể chỉ được các loại IOException
hoặc SQLException
. Tôi vẫn không quá nhiệt tình về việc sử dụng kiểu này, nhưng nó không gây ra mã xấu như trong Java 6 (nơi nó sẽ buộc bạn phải có chữ ký phương thức là siêu lớp mà các ngoại lệ mở rộng).
Bất chấp tất cả những thay đổi này, nhiều công cụ phân tích tĩnh (Sonar, PMD, Checkstyle) vẫn đang thực thi các hướng dẫn kiểu Java 6. Đó không phải là một điều xấu. Tôi có xu hướng đồng ý với một cảnh báo những điều này vẫn được thi hành, nhưng bạn có thể thay đổi mức độ ưu tiên của chúng thành chính hoặc phụ theo cách nhóm của bạn ưu tiên chúng.
Nếu trường hợp ngoại lệ nên được kiểm tra hoặc không kiểm soát ... đó là một vấn đề của g r e một t tranh luận rằng người ta có thể dễ dàng tìm thấy vô số bài đăng trên blog chiếm mỗi bên của cuộc tranh cãi. Tuy nhiên, nếu bạn đang làm việc với các ngoại lệ được kiểm tra, có lẽ bạn nên tránh ném nhiều loại, ít nhất là theo Java 6.