Heiner KückerNull-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-PointerObjekt-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 ErweiterungenEs 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-ObjectExplizites 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 NullPointerExceptionsNullPointerExceptions 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 MethodenDer 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-ParameternDenkbar 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-ObjectNun 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. KonsequenzDie 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
Nachteile
WeiterentwicklungKonsequent 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.AlternativeMan 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 wurdeEigentlich 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.
|