Các chi tiết cụ thể của trường hợp Java cho trường hợp này (có lẽ rất giống với trường hợp C #) là để làm thế nào trình biên dịch Java xác định nếu một phương thức có thể trả về.
Cụ thể, các quy tắc là một phương thức có kiểu trả về phải không thể hoàn thành bình thường và thay vào đó phải luôn luôn hoàn thành đột ngột (đột ngột ở đây chỉ ra thông qua câu lệnh trả về hoặc ngoại lệ) trên JLS 8.4.7 .
Nếu một phương thức được khai báo là có kiểu trả về, thì lỗi thời gian biên dịch xảy ra nếu phần thân của phương thức có thể hoàn thành bình thường. Nói cách khác, một phương thức có kiểu trả về chỉ phải trả về bằng cách sử dụng câu lệnh return cung cấp giá trị trả về; nó không được phép "thả ra khỏi phần cuối của cơ thể" .
Trình biên dịch xem xét liệu có thể chấm dứt bình thường hay không dựa trên các quy tắc được xác định trong JLS 14,21 Báo cáo không thể truy cập vì nó cũng xác định các quy tắc để hoàn thành bình thường.
Đáng chú ý, các quy tắc cho các câu lệnh không thể truy cập tạo ra một trường hợp đặc biệt chỉ dành cho các vòng lặp có true
biểu thức hằng xác định :
Một câu lệnh while có thể hoàn thành bình thường nếu ít nhất một trong những điều sau đây là đúng:
Vì vậy, nếu while
câu lệnh có thể hoàn thành bình thường , thì câu lệnh trả về bên dưới là cần thiết vì mã được coi là có thể truy cập và bất kỳ while
vòng lặp nào không có câu lệnh ngắt có thể tiếp cận hoặc true
biểu thức hằng được coi là có thể hoàn thành bình thường.
Những quy định này có nghĩa là bạn while
tuyên bố với một biểu thức đúng liên tục và không có một break
là không bao giờ được coi là hoàn thành bình thường , và vì vậy bất kỳ mã bên dưới nó là không bao giờ được coi là có thể truy cập . Kết thúc của phương thức nằm dưới vòng lặp và vì mọi thứ bên dưới vòng lặp đều không thể truy cập được, nên kết thúc của phương thức, và do đó phương thức không thể hoàn thành bình thường (đó là điều mà trình biên dịch tìm kiếm).
if
mặt khác, các tuyên bố không có sự miễn trừ đặc biệt liên quan đến các biểu thức không đổi được dành cho các vòng lặp.
Đối chiếu:
// I have a compiler error!
public boolean testReturn()
{
final boolean condition = true;
if (condition) return true;
}
Với:
// I compile just fine!
public boolean testReturn()
{
final boolean condition = true;
while (condition)
{
return true;
}
}
Lý do cho sự khác biệt khá thú vị và là do mong muốn cho phép các cờ biên dịch có điều kiện không gây ra lỗi biên dịch (từ JLS):
Người ta có thể mong đợi câu lệnh if được xử lý theo cách sau:
Một câu lệnh if-then có thể hoàn thành bình thường nếu ít nhất một trong những điều sau đây là đúng:
Câu lệnh then có thể truy cập được nếu câu lệnh if-then có thể truy cập được và biểu thức điều kiện không phải là biểu thức hằng có giá trị là sai.
Một câu lệnh if-then-other có thể hoàn thành bình thường nếu câu lệnh then có thể hoàn thành bình thường hoặc câu lệnh khác có thể hoàn thành bình thường.
Câu lệnh then có thể truy cập được nếu câu lệnh if-then-other có thể truy cập được và biểu thức điều kiện không phải là biểu thức hằng có giá trị là sai.
Câu lệnh other có thể truy cập được nếu câu lệnh if-then-other có thể truy cập được và biểu thức điều kiện không phải là biểu thức hằng có giá trị là đúng.
Cách tiếp cận này sẽ phù hợp với việc xử lý các cấu trúc điều khiển khác. Tuy nhiên, để cho phép câu lệnh if được sử dụng thuận tiện cho mục đích "biên dịch có điều kiện", các quy tắc thực tế khác nhau.
Ví dụ, câu lệnh sau dẫn đến lỗi thời gian biên dịch:
while (false) { x=3; }
bởi vì tuyên bố x=3;
không thể đạt được; nhưng trường hợp tương tự bề ngoài:
if (false) { x=3; }
không dẫn đến lỗi thời gian biên dịch. Trình biên dịch tối ưu hóa có thể nhận ra rằng câu lệnh x=3;
sẽ không bao giờ được thực thi và có thể chọn bỏ qua mã cho câu lệnh đó khỏi tệp lớp được tạo, nhưng câu lệnh x=3;
không được coi là "không thể truy cập" theo nghĩa kỹ thuật được chỉ định ở đây.
Lý do căn bản cho cách xử lý khác nhau này là cho phép các lập trình viên định nghĩa "các biến cờ" như:
static final boolean DEBUG = false;
và sau đó viết mã như:
if (DEBUG) { x=3; }
Ý tưởng là có thể thay đổi giá trị DEBUG từ false thành true hoặc từ true thành false và sau đó biên dịch mã chính xác mà không có thay đổi nào khác đối với văn bản chương trình.
Tại sao câu lệnh break có điều kiện dẫn đến lỗi trình biên dịch?
Như được trích dẫn trong các quy tắc khả năng tiếp cận vòng lặp, một vòng lặp while cũng có thể hoàn thành bình thường nếu nó chứa một câu lệnh break có thể tiếp cận. Kể từ khi các quy tắc cho các reachability của một if
của tuyên bố sau đó khoản không mất tình trạng của if
xem xét ở tất cả, chẳng hạn một điều kiện if
tuyên bố của sau đó khoản luôn được coi là có thể truy cập.
Nếu break
có thể truy cập, thì mã sau vòng lặp một lần nữa cũng được coi là có thể truy cập. Vì không có mã có thể truy cập dẫn đến kết thúc đột ngột sau vòng lặp, nên phương thức sau đó được coi là có thể hoàn thành bình thường, và do đó trình biên dịch đánh dấu nó là một lỗi.