Heiner Kücker

Null-Pointer in Java

Home

Java-Seite

   Weiterentwicklung_Java

     NullPointer

     Algorithmische
     Symmetrie

     Finder_Feature

     Multiple_Interface
     Implementationen

     Proxy_Methoden

     Speicher_Klassen

   WebCam_Demo

   JSP WorkFlow
   PageFlow FlowControl
   Page Flow Engine
   Control_and_Command

   JSP_Spreadsheet

   Domain Parser

   Codegenerator_für
   hierarchische
   Datenstrukturen

   Expression_Engine
   Formula_Parser

   State Transition Engine

   AspectJ

   Java_Explorer

   DBF_Library

   Kalender_Applet

   SetGetGen

   BeanSetGet

   CheckPackage

   LineNumbers

   GradDms

   Excel-Export

   StringTokenizer

   JspDoc

   JspCheck

   JSP-Schulung
   Java Server Pages
   Struts

   AsciiTabellenLayouter

   StringSerial

   Silbentrennung

   JDBC_Schlüssel-
   Generierung

   bidirektional/
   unidirektional
   gelinkte Liste

   Java_Sitemap
   Generator

   XmlBuilder

   RangeMap

Alaska-XBase++-Seite

Projekte

Philosophien
Techniken


Konzepte

Sudoku

Kontakt /
Impressum


Links

SiteMap





Letzte Aktualisierung:
24.08.2005

Konzept Erweiterung Java um Behandlung Null-Pointer

Objekt-Referenzen in Java können den Zustand null annehmen. Das bedeutet, daß die jeweilige Objekt-Referenz-Variable auf kein Objekt zeigt.

Einerseits kann der null-Zustand ein Fehler sein, andererseits ist es möglich, daß der null-Zustand eine besondere semantische Bedeutung hat.

Wenn zum Beipiel eine Methode einen String mit einem Datum einparst, kann diese Methode für das Datum einen Null-Pointer zurückgeben, falls der String leer ist oder nicht dem Datums-Format entspricht. Bei der Rückgabe eines NullPointers kann nicht erkannt werden, ob das Datum falsch war oder ob kein Datum eingegeben wurde. Falls es erlaubt ist, kein Datum einzugeben, ist so der Zustand nicht eindeutig. Ein Datum numerisch Null entspricht in Java dem 1.1.1970 00:00 Uhr. Es ist nicht sinnvoll, wenn die Parse-Methode dieses Datum als Ersatz für den Status "leer" zurückliefert. Eine explizite Definition für undefinierten Zustand wie numerisch NaN (Not a Number) gibt es nicht. Er muss explizit codiert werden.

Ein weiteres Beispiel ist ein Boolean-Object. Es kann den Wert true oder false annehmen. Dazu kommt der Wert null.

Auch in SQL gibt es nullable-Felder. Bei der Anbindung von Datenbanken über JDBC stellt sich also auch das Problem der Null-Pointer. Es ist ein unterschied ob ein Feld NULL oder numerisch der Wert Null ist.

Elementartypen können keinen null-Zustand annehmen. Sie sind somit sicher gegenüber NullPointerExceptions, können den null-Zustand aber auch nicht als zusätzliche Zustands-Codierung benutzen.

Problem 1:

Wenn ein Objekt null ist, obwohl es nicht null sein darf, kommt es beim Methoden-Aufruf zur NullPointerException. Diese tritt aber erst beim Anwenden des Objektes (Methodenaufruf) und nicht schon beim Zuweisen/Setzen null auf.
Stellen Sie sich folgendes Beispielprogramm vor:

String a = null
...
String b = a //hier wird der Fehler gemacht
...
b.toString();//hier tritt der Fehler auf

Zwischen den dargestellten Programmzeilen liegen jeweils zahlreiche andere Programmzeilen.
Im Programmcode kann die Stelle, an welcher die fehlerverursachende Null-Setzung erfolgte, weit von der Stelle, an der der Fehler auftritt, dem Methodenaufruf, entfernt sein (auch in anderer Methode).

Problem 2:

Bei der Ausnutzung des null-Zustandes als spezielle Codierung muss dies neben dem Code dokumentiert werden. Oftmals wird dies nicht getan. Es gibt keine Unterstützung in Java für die Bedeutung des null-Zustandes. In einer grossen Applikation kann es passieren, daß der null-Zustand an verschiedenen Stellen unterschiedlich interpretiert wird. Dies ist eine potentielle Fehler-Quelle und kann nur durch Tests gefunden werden.

Problem 3:

Angenommen, die Programmiererin hat sich entschieden, den null-Zustand als Codierung auszunutzen. Braucht sie aber eine Codierung für falsch (falsches Datumsformat) und eine Codierung für leer (kein Datum eingegeben) ist der null- Zustand nicht eindeutig. Es kann passieren, daß der Programmiererin dieses nicht bewußt ist und sie es nicht berücksichtigt. Um dieses Problem zu lösen muß eine spezielle Status-Klasse (Pattern Value-Object) benutzt werden.


Vorschläge für sprachliche Erweiterungen

Es wird ein neuer Modifier notnullable eingeführt.

public notnullable class ... {

Bei der Deklaration einer Klasse angewendet, sagt der Modifier aus, daß alle Instanzen dieser Klasse nicht null sein dürfen.

private notnullable String = "" ;

Bei der Deklaration einer Variable angewendet, sagt der Modifier aus, daß dieses Objekt niemals null sein darf.

Daraus folgt, daß alle notnullable-Objekte bei der Deklaration initialisiert werden müssen.

Zur Kompatibilität mit altem Code eignet sich die optionale Verwendung mit einem Compilerschalter.


Zuweisen nullable-Object zu notnullable-Object

Explizites casten (wirft NullPointerException):

notnullObj = (notnullable) nullObj;

Beim Versuch einen Null-Pointer per Cast einer notnullable-Variable zuzuweisen, wird eine NullPointerException geworfen.

Der Compiler verlangt hier keinen try-catch-Block.

Auscodiert könnte diese Zuweisung folgenden Code generieren:

if ( nullObj == null ) { throw new NullPointerException( "nullObj" ) ; }
notnullObj = nullObj;


Schützen von Operationensblöcken vor NullPointerExceptions

NullPointerExceptions treten beim Aufruf von Methoden des jeweiligen Objektes auf. Deshalb ist eine vom Compiler erzwungene Prüfung auf null vor Methoden- Aufruf wünschenswert.

Der Compiler verlangt, eventuell optional über einen Compilerschalter, den Schutz von Methoden-Aufrufen auf notnullable-Objekte. Dies könnte durch Zuordnung der NullPointerException zu den checked-Exceptions oder durch das Verlangen eines speziellen notnull-Blockes realisiert werden.

try { //checked Exception
 nullObj.method();
}catch(NullPointerException e){
 ...
}

oder
//Source-Code                     | //Pseudo-Code
try notnull { // notnull-Block    | if(nullObj!=null){
  nullObj.method();               |   nullObj.method();
}isnull{                          | }else{
  ...                             |   ...
}                                 | }

notnullable-Object als Rückgabe-Wert von Methoden

Der Einsatz des Modifier notnullable für Methoden könnte sichern, daß diese Methode niemals einen NullPointer zurückgeben darf.

public notnullable String getName() {
...

Dies deklariert den Rückgabe-Wert als notnullable.

Damit kann diese Methode ungeprüft zur Zuweisung an notnullable-Objekte verwendet werden. Andererseits wird das return-Statement in der Methode wie eine Zuweisung an ein notnullable-Objekte über explizites casten oder einen try-null- Block abgesichert.


Modifier notnullable bei Methoden-Parametern

Denkbar ist die Anwendung des Modifier notnullable bei Methoden-Parametern, die das Übergeben eines Null-Objektes verhindert:

public void execute( notnullable String strPa ) {
 ...

Beim Aufruf der Methode mit einem Null-Pointer wird eine Null-Pointer-Exception geworfen. Alternativ könnte der Compiler verlangen, daß nur notnullable-Parameter übergeben werden. Das heißt, nullable-Objekte müssten explizit in notnullable-Objekte gecastet werden.
Dann gelten die Regeln des Castens:

obj.excute( (notnullable) nullableString );


Möglichkeit zur Definition der Bedeutung von null bei nullable-Object

Nun zur Möglichkeit, das der Status null einer Objekt-Referenz explizit erlaubt ist.

In diesem Fall ist ein Compiler-geprüfter Zwang zur Definition der Bedeutung von null sinnvoll.

Hier könnte man einen Modifier nullable einsetzen. Dieser verlangt, daß dem Null-Zustand ein expliziter Name gegeben werden muss:

public nullable class ...
nullstate <Bezeichner>;

zum Beispiel

public nullable class NullableBoolean {
nullstate FALSE;

Hier sagt die Deklaration nullstate aus, daß ein NullPointer als logisch false zu interpretieren ist.

Daraus folgt ein Verbot des Vergleichs mit null:

if (obj == null){ // Compiler-Fehler

if (obj == NullableBoolean.FALSE){ // in Ordnung

Dadurch wird die semantische Bedeutung von null implizit dokumentiert und anstelle der nicht semantisch eindeutigen Verwendung von null muß immer mit dem nullstate-Bezeichner verglichen werden. Demzuflge ist die semantische Bedeutung des nullstate an allen Stellen im Quell-Code zu erkennen. Natürlich muß der Bezeichner für den nullstate sinnvoll sein. Wenn hier etwas Falsches deklariert wird, ist der Compiler machtlos.

public nullable class NullableParsedDate {
nullstate WRONG_FORMAT;

Bei diesem Beispiel sagt der nullstate aus, daß ein nicht valider String zum Parsen übergeben wurde.


Konsequenz

Die Modifier nullable und notnullable lassen noch die implizit nullable Klassen bzw. Referenzen (Klassen ohne einen der beiden Modifier) übrig. Bei konsequenter Einführung dieses Konzepts sollte man die implizit nullable Klassen bzw. Referenzen verbieten.


Vorteile

  • weniger Fehler durch Verhindern NullPointerExceptions und Erzwingen des Auftretens der NullPointerException an der Stelle der Zuweisung anstatt beim Methoden-Aufruf


  • weniger Dokumentationsaufwand und weniger Missverständnisse durch erzwungene Deklaration der semantischen Bedeutung des nullstate


  • weniger Aufwand zum Nachvollziehen (Nach-Engineeren) vorhandenen Codes



Nachteile

  • Laufzeit-Nachteile und Speicher-Nachteile durch erzwungene Initialsierung. Gilt aber nicht für Methoden-lokale Referenzen(Variable).


  • mehr Aufwand im Compiler. Das sollte dem Anwedungsprogrammierer aber egal sein.


  • Mehr Aufwand beim Codieren, der aber durch die höhere Sicherheit kompensiert wird



Weiterentwicklung

Konsequent weiter gedacht, ist die Behandlung der Null-Pointer eigentlich eine spezielle Form des Design by Contract. Über DbC reiche ich demnächst einen Text nach.


Alternative

Man könnte das Problem der NullPointerException einfach so lösen, daß NullPointer verboten werden. Jede Variable(Referenz) muß sofort initialisiert werden. Das Schlüsselwort null wird aus der Sprache entfernt (Was man nicht hat, kann man nicht zuweisen).

Diese Lösung hat folgenden Nachteil:

Wenn eine NullPointerException bzw. MissingObjectException auftritt, so ist nicht das fehlende Objekt, sondern der falsche Wert das Problem:

Class var = null;

if (...){
  var = Object;
}

var.methode() // hier tritt der Fehler auf, wenn if nicht durchlaufen wurde
Eigentlich sagt der NullPointer aus, das sich der Methoden-Aufruf auf das falsche Objekt bezieht. Äußert sich eben als NullPointerException.

Der Schutz vor NullPointerException bedeutet dann, daß das Programm gegen falsche Werte stabilisiert wird. Dafür sind aber eigentlich die try-catch da. Den Fehler müsste man über UNIT-Test ausschließen.

So gesehen sind NullPointerException´s ganz in Ordnung.

Dafür ist Herangehensweise, den Fehler schon beim Zuweisen zu provozieren, vorteilhafter.


Hier der Thread auf de.comp.lang.java zu diesem Thema