2011-02-07 26 views
13

Tôi đã tìm thấy một lỗi khác thường trong mã của mình gần đây thông qua thử nghiệm ngẫu nhiên. Vì vậy, tôi đã thực hiện một trường hợp thử nghiệm cho nó.Trong toán tử ternary của Java, đối số đầu tiên có thể được đánh giá ngay cả khi biểu thức dẫn đến giá trị sai?

Đây là trường hợp thử nghiệm của tôi:

SampleRequest request = new SampleRequest(); 
    request.setA(null); 
    request.setB(null); 
    assertEquals(null, request.getAOrB()); 

A và B được định nghĩa là loại java.lang.Integer và có phương pháp setter trực tiếp để thiết lập giá trị của họ vào yêu cầu.

Ngoài ra còn có một điều tra liên quan. Nó có một giá trị nguyên nguyên, và một phương thức được sử dụng trong mã này. Tôi sẽ đăng các phần có liên quan tại đây:

enum Swapper { 
public int c; 
Swapper findSwapperToUse(final int a) { 
    for(Swapper swapper : values()) { 
     if(swapper.c == a) { 
      return swapper; 
     } 
    } 
    return null; 
} 
} 

Bây giờ, đây là phương pháp gây nhầm lẫn. Gọi phương thức thử nghiệm trên phương thức đó dẫn đến một NPE, nhưng trên dòng cuối cùng của phương thức.

public class SampleRequest { 
    private Integer A; 
    private Integer B; 

    public void setA(final Integer A) { 
     this.A = A; 
    } 

    public void setB(final Integer B) { 
     this.B = B; 
    } 


public Integer getAOrB() { 
    return A != null ? Swapper.findSwapperToUse(A).c 
     : B; 
} 
} 

Trong thử nghiệm, cả A và B được đặt thành rỗng. Do đó, A! = Null trả về false. Tuy nhiên, tôi nhận được một NullPointerException tại số dòng cho dòng: B.

Tôi đoán là vì lý do nào đó biểu thức đầu tiên, Swapper.findSwapperToUse (A) .c, đang được đánh giá, và do đó hàm A.intValue() được gọi thông qua autoboxing, dẫn đến NullPointerException trên giá trị null. Thông qua gỡ lỗi, nó được biết rằng findSwapperToUse() không được gọi.

Tuy nhiên, theo này questionthis không nên xảy ra: Java ternary (immediate if) evaluation

Biểu thức toán hạng không chọn không được đánh giá cho rằng đánh giá cụ thể của biểu thức điều kiện.

Trả về giá trị rỗng (B) sẽ không dẫn đến NullPointerException - hoàn toàn tốt để trả về kết quả rỗng tại đây.

Chuyện quái gì đang xảy ra?

EDIT: Tôi quên thêm rằng tôi thay đổi mã để tránh điều này bằng cách sử dụng một tuyên bố thẳng lên nếu - đoạn mã sau làm việc như mong đợi:

public Integer getAOrB() { 
    if(A != null) { 
     return Swapper.findSwapperToUse(A).c; 
    } 
    return B; 
} 
+2

Mã bạn đã nhất định sẽ không biên dịch (ví dụ một thiếu kiểu trả về). Vui lòng đưa ra một ví dụ ngắn nhưng * hoàn chỉnh * và chúng ta sẽ thấy những gì đang diễn ra. –

+0

Làm thế nào bạn có thể được _ABSOLUTELY_ nhất định rằng A là null? –

+0

Tôi đã cung cấp loại trả lại, xin lỗi - đó là Swapper. @Thor - vì thử nghiệm (request.setA (null), request.setB (null)) – MetroidFan2002

Trả lời

22

Tôi đoán vấn đề là do thực tế là trình biên dịch suy luận kiểu biểu hiện toàn bộ

A != null ? Swapper.findSwapperToUse(A).c : B 

dưới dạng int từ loại Swapper.c và do đó cố gắng áp dụng chuyển đổi unboxing thành B.

Dưới đây là trích đoạn liên quan từ the JLS, §15.25:

  • Nếu không, nếu toán hạng thứ hai và thứ ba có các loại có mui trần (§5.1.8) với các loại số, sau đó có một số trường hợp:
    • ...
    • Nếu không, chương trình khuyến mãi số nhị phân (§5.6.2) được áp dụng cho các toán hạng loại, và loại của biểu thức điều kiện là pr loại bỏ qua của toán hạng thứ hai và thứ ba. Lưu ý rằng quảng cáo số nhị phân thực hiện chuyển đổi unboxing (§5.1.8) và chuyển đổi bộ giá trị (§5.1.13).

Bạn có thể ngăn chặn nó bằng cách thêm các diễn viên sau:

A != null ? (Integer) Swapper.findSwapperToUse(A).c : B 
+1

Vâng phát hiện Sir –

+0

Đó là vô lý, nhưng đó là những gì nó được! Cảm ơn. – MetroidFan2002

+2

:(Nên có một lá cờ trình biên dịch để vô hiệu hóa autoboxing để nắm bắt những thứ này. –

0

phương pháp findSwapperToUse của bạn đang trở lại null, và bạn không thể làm null.c.

Để đảm bảo điều này, tôi sẽ thay đổi mã của bạn để đọc:

public Integer getAOrB() { 
    if(A != null) { 
     Swapper foundSwapper = Swapper.findSwapperToUse(A); 
     return foundSwapper.c; 
    } 
    return B; 
} 
+0

Đó cũng là những gì tôi nghĩ, nhưng nó sẽ không giải thích được chút nào ở cuối câu hỏi. –

+0

OP nói rõ ràng rằng phương thức này không được gọi. Đó là không có gì ngạc nhiên khi xem xét rằng A là null, vì vậy nó không thể là một đối số cho một hàm chấp nhận 'int'. –

+0

@Sergey oops bỏ lỡ bit đó :) – Rich

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