2012-10-06 33 views
11

Tôi đã xem qua một số NullPointerException thực sự kỳ lạ vào một ngày khác gây ra bởi một kiểu truyền bất ngờ trong toán tử bậc ba. Với này (vô dụng gương mẫu) chức năng:NullPointerException thông qua hành vi tự động-boxing của toán tử ternary Java

Integer getNumber() { 
    return null; 
} 

Tôi đã chờ đợi hai đoạn mã sau vào được chính xác giống hệt nhau sau khi biên dịch:

Integer number; 
if (condition) { 
    number = getNumber(); 
} else { 
    number = 0; 
} 

vs

Integer number = (condition) ? getNumber() : 0; 

.

Hóa ra, nếu conditiontrue, các if -statement hoạt động tốt, trong khi opration ternary trong đoạn mã thứ hai ném một NullPointerException. Dường như hoạt động ba năm đã quyết định nhập cả hai lựa chọn vào int trước khi tự động kết quả trở lại thành một số Integer!?! Trên thực tế, nếu tôi truyền một cách rõ ràng 0 đến Integer thì ngoại lệ sẽ biến mất. Nói cách khác:

Integer number = (condition) ? getNumber() : 0; 

là không giống nhau như:

Integer number = (condition) ? getNumber() : (Integer) 0; 

.

Vì vậy, có vẻ như có sự khác biệt mã byte giữa toán tử bậc ba và số if-else tương ứng (điều tôi không mong đợi). Điều này đặt ra ba câu hỏi: Tại sao có sự khác biệt? Đây có phải là một lỗi trong việc thực hiện ternary hoặc là có một lý do cho các loại cast? Có một sự khác biệt, là hoạt động bậc ba ít hay nhiều hơn một mức tương đương if -statement (Tôi biết, sự khác biệt không thể lớn, nhưng vẫn còn)?

+6

Bạn nghiêm túc tin rằng có lỗi trong toán tử bậc ba thay vì hiểu * của bạn về việc sử dụng tài liệu và hạn chế của toán tử? Bạn nghĩ gì về khả năng xảy ra thực tế này? Xem xét việc thay đổi tiêu đề câu hỏi của bạn thành "sự hiểu lầm trong cách toán tử ternary hoạt động". –

+1

Đây là lý do tại sao tôi hỏi câu hỏi tại sao trình biên dịch quyết định rằng getNumber() và 0 cả hai nên đánh giá vào một int nếu tôi gán kết quả cho một số nguyên. Đối với tôi, nó hoàn toàn không có ý nghĩa để bỏ hai đối số đến hạn chế hơn của hai loại TRƯỚC KHI so sánh thay vì loại thực sự yêu cầu SAU so sánh. Tại sao đi qua unboxing và sau đó reboxing getNumber()? –

+0

Nó không tạo ra sự khác biệt nào về bạn và tôi nghĩ * nên * xảy ra. Thay vào đó tất cả những vấn đề là những gì được ghi chép rõ ràng trong JLS. –

Trả lời

13

Theo JLS: -

Loại một biểu thức điều kiện được xác định như sau:

  • Nếu toán hạng thứ hai và thứ ba có cùng loại (trong đó có thể là loại null) , thì đó là loại biểu thức có điều kiện .
  • Nếu một trong hai toán hạng thứ hai và thứ ba thuộc loại T nguyên thủy, và loại kia là kết quả của việc áp dụng chuyển đổi đấm bốc
    (§5.1.7) thành T, thì loại biểu thức có điều kiện là T .
+1

Vì vậy, nó được cho là theo cách này. Để lại câu hỏi liệu có sự khác biệt về hiệu suất giữa hai loại, đặc biệt là cho tất cả các sự kiện tự động (un) -boxing xảy ra. –

+0

@Markus .. Vâng, bạn không thể nói liệu có một sự khác biệt hiệu suất hay không .. Điều này chắc chắn cung cấp cho các lập trình viên dễ dàng mã hóa .. Nhưng có, vì điều này bao gồm một phần nhỏ của 'Unboxing' .. mà không phải là trong trường hợp của if-else .. Vì vậy, có thể hiệu suất sẽ thấp .. –

+0

@Markus .. Nhưng với toán tử bậc ba này chủ yếu được sử dụng chỉ trong trường hợp điều kiện câu lệnh 'đơn'.Vì vậy, nó không phải là nhiều của một lo lắng .. Tất nhiên bạn sẽ không thể di chuyển toàn bộ biểu thức từ một khối if-else để điều hành ternary .. Vì vậy, cả hai đều có ưu và nhược điểm .. –

11

Vấn đề là:

Integer number = (condition) ? getNumber() : 0; 

Buộc một unboxing và reboxing về kết quả getNumber(). Điều này là do phần sai của ternary (0) là một số nguyên, vì vậy nó cố chuyển đổi kết quả của getNumber() thành một int. Trong khi các trường hợp sau không:

Integer number = (condition) ? getNumber() : (Integer) 0; 

Đây không phải là lỗi, chỉ cần cách Java chọn làm việc.

+0

Chính xác, nhưng tại sao? –

+2

@Markus Xem [Đặc tả ngôn ngữ Java 15.25] (http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.25) 'Nếu một trong hai thứ hai và thứ ba toán hạng là kiểu nguyên thủy T, và kiểu kia là kết quả của việc áp dụng chuyển đổi quyền anh (§5.1.7) thành T, thì kiểu biểu thức có điều kiện là T.' – halex

2

Đây là cách hoạt động của nó. Toán tử bậc ba là không có nghĩa là tương đương với câu lệnh if thông thường. Nội dung của các số ifelsebáo cáo, trong khi các phần sau ?: là các biểu thức , bắt buộc phải đánh giá cùng loại.

Đặt một cách khác: a = b ? c : d không được coi là tương đương với if (b) a = c; else a = d;. Thay vào đó, b ? c : d là một biểu thức riêng và việc gán kết quả của nó thành a sẽ không ảnh hưởng đến kết quả.

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