2013-02-20 23 views
5

Jeśli uruchomić poniższy kod,unboxing null pudełkowej obiekt rzuca nieoczekiwana NullPointerException

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; 
     } 
    } 
} 

dostaniesz następujące StackTrace,

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

Jak to nie ma ostrzeżenie kompilatora lub cokolwiek? IMHO to dość nieprzyjemna subtelność z rozpakowywaniem, a może po prostu jestem naiwna.


Dodawanie do odpowiedzi udzielonej przez @Javier, jeśli używasz Eclipse, należy wykonać następujące czynności, aby umożliwić w ten sposób:

  1. przejdź do Okno>Preferencje>Java>Kompilator>Błędy/ostrzeżenia
  2. Rozwiń Potencjalne problemy programistyczne
  3. Przegubowe Boks i unboxing konwersje albo "Ostrzeżenie" lub "Error"
  4. puknij "OK"
+0

Nie rozumiem. Pytasz, dlaczego dzieje się NPE, czy to tylko rant? Jakie byłoby konkretne pytanie podlegające odpowiedzi? – madth3

Odpowiedz

5

nie wiem co IDE używasz , ale Eclipse ma opcję włączenia ostrzeżenia o boksowaniu i rozpakowywaniu konwersji. Nie jest możliwe wykrycie go jako dostępu do wskaźnika pustego, ponieważ wartość null nie jest natychmiast rozpakowywana, ale przez Bar.getId().

Wyrażenie typu Integer jest rozpakowanych do int linii
Foo.java 3

+0

Używam Eclipse przez ~ 3 lata i nie wiedziałem o tym! Słodkie! – mre

+2

To denerwujące, że Eclipse nie może włączyć rozpakowywania ostrzeżeń bez boksowania! https://bugs.eclipse.org/bugs/show_bug.cgi?id=163065 – Kyle

3

Jeśli spróbujesz użyć dowolnej metody na null lub zrobić coś, co nie ma sensu z null, rzuca NullPointerException.

Autounboxing jest realizowany za pomocą metody [Integer object].intValue() (lub podobnej), więc rzuca NullPointerException, ponieważ nie można wywołać metody metodą null.

Mam nadzieję, że to pomoże!

+1

+1 za ten fragment o 'intValue' za kulisami, dziękuję – mre

0

NullPointerException to RuntimeException niż IDE nie może wykryć podczas kompilacji kodu.

Zamiast tego dobrą praktyką jest sprawdzenie wartości zerowej przed rozpakowaniem.

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

Wydaje się być całkowicie rozsądnym wyjątkiem czasu wykonywania. Jeśli Twój główny kod:

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

Nikt nie byłby zaskoczony wyjątkiem wskaźnika pustego. Klasa Bar zwraca wartość null, a wskaźnik obiektu zerowego nie może zostać przekształcony w prostą wartość. Implementacja klasy Bar może zostać zmieniona w celu zainicjowania identyfikatora na wartość inną niż null.Ten blok kodu może być skompilowany niezależnie od klasy Bar, więc założenia dotyczące dynamicznych operacji klasy Bar z pewnością nie powinny być kodowane do tego bloku kodu.

Jest to prawdopodobnie oczywiste, ale prawdziwym rozwiązaniem jest użycie int dla członka id, zamiast Integer. Ten więc, nie ma problemu:

private static class Bar{ 
    private final int id; 

    public Bar(){ 
     this(0); 
    } 

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

    public int getId(){ 
     return id; 
    } 
} 

(ale przypuszczam, że już byli świadomi tego :-))

+0

Nie powiedziałem, że wyjątek był nierozsądny, powiedziałem, że było nieoczekiwanie, zwłaszcza, że ​​moje IDE domyślnie zignorowało to. – mre

+0

OK, próbowałem ominąć to, co jest "oczekiwane" i "nieoczekiwane". Jest to wyjątek w czasie wykonywania, a nie coś, co może być koniecznie określone na podstawie analizy statycznej. Jak można się spodziewać, że IDE zachowa się w tej sytuacji? – AgilePro

+0

Spodziewałem się, że IDE przynajmniej ostrzeże mnie domyślnie. Tak jak powiedziałem, myślę, że jest to dość subtelny wyjątek środowiska wykonawczego. – mre

4

wydaje się, że takie zachowanie jest udokumentowana w JDK™ 5.0 Documentation,

.. w dużej mierze można zignorować rozróżnienie między int i Integer, z kilka zastrzeżeń. Wyrażenie Integer może mieć wartość pustą. Jeśli program próbuje automatycznie anulować polecenie, spowoduje wyświetlenie NullPointerException.

0

Boks to nic innego jak cukier syntaktyczny do rzucania obiektu typu Integer do lokalnego odpowiednika "int" . Język nie może mieć wartości null, ale obiekty mogą. Mechanizm boksu nie będzie w tych przypadkach blokował NullPointerExceptions.

Powiązane problemy