2013-02-20 21 views
5

Nếu bạn chạy đoạn mã sau,Unboxing một đối tượng đóng hộp rỗng ném NullPointerException bất ngờ

public class Foo{ 
    public static void main(String[] args){ 
     int id = new Bar().getId(); // throws unexpected NullPointerException 
    } 

    private static class Bar{ 
     private final Integer id; 

     public Bar(){ 
      this(null); 
     } 

     public Bar(Integer id){ 
      this.id = id; 
     } 

     public Integer getId(){ 
      return id; 
     } 
    } 
} 

bạn sẽ nhận được stacktrace sau,

Exception in thread "main" java.lang.NullPointerException 
    at Foo.main(Foo.java:3) 

Tại sao không có cảnh báo trình biên dịch hoặc bất cứ điều gì? IMHO đó là một sự tinh tế khá khó chịu với unboxing, hoặc có lẽ tôi chỉ ngây thơ.


Thêm vào câu trả lời cung cấp bởi @Javier, nếu bạn đang sử dụng Eclipse, bạn cần phải làm như sau để kích hoạt tính năng này:

  1. Navigate to Window>Preferences>Java>Trình biên dịch>Lỗi/cảnh báo
  2. Mở rộng vấn đề tiềm năng lập trình
  3. Chuyển đổi Boxing và chuyển đổi unboxing hoặc là "Cảnh báo", hoặc "Lỗi"
  4. Tap "OK"
+0

Tôi không hiểu. Bạn có hỏi tại sao NPE xảy ra hay đây chỉ là một rant? Câu hỏi có thể trả lời cụ thể là gì? – madth3

Trả lời

5

Tôi không biết những gì IDE bạn đang sử dụng , nhưng Eclipse có một tùy chọn để kích hoạt cảnh báo về chuyển đổi quyền anh và unboxing. Nó không thể phát hiện nó như là một con trỏ null truy cập, vì null không phải là không lập tức unboxed, nhưng thông qua Bar.getId().

Các biểu hiện của kiểu Integer được mở hộp vào int dòng
Foo.java 3

+0

Tôi đã sử dụng Eclipse trong ~ 3 năm và tôi không biết điều đó! Ngọt! – mre

+2

Thật khó chịu khi nhật thực không thể bật cảnh báo unboxing mà không cần đấm bốc! https://bugs.eclipse.org/bugs/show_bug.cgi?id=163065 – Kyle

3

Nếu bạn cố gắng sử dụng bất kỳ phương pháp trên null hoặc làm bất cứ điều gì mà không có ý nghĩa với một null, nó ném một NullPointerException.

Tự động phát hộp được thực hiện với phương pháp [Integer object].intValue() (hoặc tương tự), vì vậy, nó ném một NullPointerException vì bạn không thể có null gọi phương thức.

Hy vọng điều này sẽ hữu ích!

+1

+1 cho bit đó về 'intValue' đằng sau hậu trường, cảm ơn – mre

0

NullPointerExceptionRuntimeException so với IDE không thể phát hiện khi biên dịch mã.

Thay vào đó, thực hành tốt là kiểm tra null trước khi bỏ hộp.

int getId(){ 
    if(id!=null){ 
     return id; 
    } 
    // return other or throw a checked exception. 
} 
0

Dường như ngoại lệ thời gian chạy hoàn toàn hợp lý. Nếu mã chính của bạn là:

public static void main(String[] args){   
    Integer idObj = new Bar().getId(); 
    int id = idObj; // throws NullPointerException 
} 

Không ai ngạc nhiên về ngoại lệ con trỏ null. Lớp Bar trả về một giá trị rỗng, và một con trỏ đối tượng null không thể được chuyển thành một giá trị đơn giản. Việc thực hiện lớp Bar có thể được thay đổi để khởi tạo id thành một giá trị không null.Khối mã này có thể được biên dịch độc lập với lớp Bar và do đó các giả định về hoạt động năng động của lớp Bar chắc chắn không được mã hóa vào khối mã này.

Điều này có thể hiển nhiên, nhưng giải pháp thực sự là sử dụng int cho thành viên id thay vì Integer. Này, sau đó, không có vấn đề:

private static class Bar{ 
    private final int id; 

    public Bar(){ 
     this(0); 
    } 

    public Bar(int id){ 
     this.id = id; 
    } 

    public int getId(){ 
     return id; 
    } 
} 

(Nhưng tôi cho rằng bạn đã hoàn toàn nhận thức được điều này :-))

+0

tôi không nói ngoại lệ là không hợp lý, tôi đã nói điều đó thật bất ngờ, đặc biệt là vì IDE của tôi đã chọn bỏ qua nó theo mặc định. – mre

+0

OK, tôi đã cố gắng để có được xung quanh những gì là "mong đợi" và "bất ngờ". Nó là một ngoại lệ thời gian chạy, không phải là một cái gì đó mà có thể nhất thiết được xác định từ phân tích tĩnh. Làm thế nào bạn mong đợi IDE để hành xử trong tình huống này? – AgilePro

+0

Tôi mong đợi IDE ít nhất sẽ cảnh báo tôi theo mặc định. Như tôi đã nói, tôi nghĩ đó là một ngoại lệ thời gian chạy khá tinh tế. – mre

4

nó xuất hiện rằng hành vi này được diễn tả trong JDK™ 5.0 Documentation,

..bạn có thể bỏ qua phần lớn sự khác biệt giữa intInteger, với một vài lưu ý. Biểu thức Integer có thể có giá trị null. Nếu chương trình của bạn cố gắng để tự động hộp số không, nó sẽ ném một NullPointerException.

0

Đấm bốc không gì khác ngoài đường cú pháp để đúc một đối tượng như Số nguyên cho số tương đương gốc 'int'. Người bản xứ không thể rỗng nhưng đối tượng có thể. Cơ chế boxing sẽ không ngăn NullPointerExceptions trong những trường hợp này.

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