Java Forum / General / February 2006
Java most wanted: Non-null variables
Jacob - 30 Jan 2006 08:25 GMT A well designed API normaly doesn't accept null as parameter values, so as a programmer you are forced to test most inputs against null and throw an IllegalArgumentException in case it is null. All these cases must be properly documented in the javadoc section with an appropriate @throws tag.
Likewise in private APIs you would do the same thing but possibly by using asserts instead of exceptions.
All of this is good and has excellent documentation value, but it is commonly ommited, in particular by inexperienced developers, or by experienced developers in a hurry, and it just seams like an issue that could be better supported by the language itself.
What about attaching a "&" character next to the variable to indicate that it cannot be null. (Just kidding :-)
Any opinions?
Bart Cremers - 30 Jan 2006 09:01 GMT Jetbrains added something like this to IntelliJ when using 1.5. They added two annotations which can be used on different levels, @Nullable and @NotNull.
@NotNull public String myMethod(@NotNull String s, @Nullable Object o) { ... }
means the method should not return null, s should not be null and o can be null. The compiler could check on this.
Bart
Roedy Green - 30 Jan 2006 10:03 GMT >Jetbrains added something like this to IntelliJ when using 1.5. They >added two annotations which can be used on different levels, @Nullable >and @NotNull. > >@NotNull >public String myMethod(@NotNull String s, @Nullable Object o) { Hey , my wish come true. Now only the money for a copy of Intellij would appear.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Stefan Schulz - 30 Jan 2006 11:06 GMT > >Jetbrains added something like this to IntelliJ when using 1.5. They > >added two annotations which can be used on different levels, @Nullable [quoted text clipped - 5 lines] > Hey , my wish come true. > Now only the money for a copy of Intellij would appear IIRC: There is an Eclipse plugin that does much the same thing.
Dimitri Maziuk - 30 Jan 2006 17:04 GMT Roedy Green sez:
>>Jetbrains added something like this to IntelliJ when using 1.5. They >>added two annotations which can be used on different levels, @Nullable [quoted text clipped - 5 lines] > Hey , my wish come true. > Now only the money for a copy of Intellij would appear. They do give it away for free if your code comes with the source (and they aren't as anal about the definition of "open source" as GNU/"Free Software" crowd).
Note, however, that in a public method that only works if code analyser has access to all code that will ever call the method.
(That's another thing that annoys me about Java: things that don't work unless you have *all* your code under one root.)
Dima
 Signature Backwards compatibility is either a pun or an oxymoron. -- PGN
Stefan Schulz - 30 Jan 2006 11:10 GMT > Jetbrains added something like this to IntelliJ when using 1.5. They > added two annotations which can be used on different levels, @Nullable [quoted text clipped - 9 lines] > > Bart Like this?
TouringMachine tm = new TouringMachine(program); String s = null;
if (tm.halts()){ s = "Halts"; }
myMethod(s, null);
The best you will be able to get is capturing the obvious case of calling myMethod with a direct null, but a null resulting in another operation will not always be detected by the compiler, take my word for it.
Also, i would not be quite as harsh as never allowing nulls. They can sometimes be safely used (for example, you could provide some callback as null if there is no callback needed).
Bart Cremers - 30 Jan 2006 12:02 GMT I know the compiler can not check it all, but it can catch part of the mistakes at compile time.
At runtime a lot more is possible using annotations. See http://oval.sourceforge.net for info on @NotNull combined with aspects (aspectj) to perform runtime validation.
Bart
David Wahler - 30 Jan 2006 15:45 GMT > > @NotNull > > public String myMethod(@NotNull String s, @Nullable Object o) { [quoted text clipped - 16 lines] > > myMethod(s, null); In a properly checked implementation, this last line should be a compile error. Notice that s is declared as a String, not an @NotNull String, and therefore cannot be directly passed to myMethod without a runtime check, perhaps something like this:
myMethod((@NotNull String) s, null); // may throw NullableCastException
> The best you will be able to get is capturing the obvious case of > calling myMethod with a direct null, but a null resulting in another > operation will not always be detected by the compiler, take my word for > it. If the implementation is designed correctly, the compiler can indeed catch all attempts to pass a null parameter. This is no different from the compile-time checks Java already does for uninitialized variables or type-checking; for example, the compiler is guaranteed to make it impossible to call myMethod(123, 456).
In particular, it is _not_ equivalent to the halting problem, as your "TouringMachine" example seemed to be implying.
> Also, i would not be quite as harsh as never allowing nulls. They can > sometimes be safely used (for example, you could provide some callback > as null if there is no callback needed). Sure; just declare that parameter as @Nullable, or whatever syntax the Powers That Be decide on.
-- David
Dimitri Maziuk - 30 Jan 2006 17:11 GMT David Wahler sez: ...
>> TouringMachine tm = new TouringMachine(program); >> String s = null; [quoted text clipped - 7 lines] > In a properly checked implementation, this last line should be a > compile error. Bull: s is a pointer and there's no way to check at compile time whether it's pointing to valid data or to 0xdeadbeef.
>> The best you will be able to get is capturing the obvious case of >> calling myMethod with a direct null, but a null resulting in another [quoted text clipped - 3 lines] > If the implementation is designed correctly, the compiler can indeed > catch all attempts to pass a null parameter. Bullshit again: if the method is public, anyone can call it, including code that has not been written yet. Compiler will have to be prescient to check that.
Dima
 Signature Backwards compatibility is either a pun or an oxymoron. -- PGN
David Wahler - 30 Jan 2006 22:01 GMT > David Wahler sez: > ... [quoted text clipped - 13 lines] > Bull: s is a pointer and there's no way to check at compile time > whether it's pointing to valid data or to 0xdeadbeef. Why not? Java doesn't have pointer arithmetic, so the only way for a reference variable to be null is if it is explicitly set to null (which should not be allowed for a non-nullable variable) or if it is an uninitialized member (again, shouldn't be allowed). If you flag those two cases as compile errors, then a non-nullable variable will never be able to contain null. Once you check all the cases (and by the way, I apologize in advance if I've forgotten any), this is a straightforward inductive proof.
> >> The best you will be able to get is capturing the obvious case of > >> calling myMethod with a direct null, but a null resulting in another [quoted text clipped - 7 lines] > code that has not been written yet. Compiler will have to be prescient > to check that. I was writing under the assumption that this feature would be incorporated into standard Java, in which case all other code would be checked in the same way. If you really want to be completely safe, the same properties should be checked at class-loading time by the JVM's bytecode verifier as well.
-- David
P.Hill - 31 Jan 2006 06:46 GMT > Bull: s is a pointer and there's no way to check at compile time > whether it's pointing to valid data or to 0xdeadbeef. The idea is that compiler can check the _caller_, given an analysis of where the values in call are coming from and all of the information about possible arguments and return values.
>>If the implementation is designed correctly, the compiler can indeed >>catch all attempts to pass a null parameter. > > Bullshit again: if the method is public, anyone can call it, including > code that has not been written yet. Compiler will have to be prescient > to check that. Despite knowing what the turing machine is, you seem to have missed the idea of where an annotation would be used by the compiler. The compiler would check the USE by the caller of such a method when compiling the _caller_ time, thus no prescience is needed.
The problem is not with some CompSci problem of completeness, but with the more nagging problem of completeness of the declarations of _every_ library both standard libraries and third party ones.
The compiler would either identify false problems or have to give up if presented with the declaration:
@NotNull public String myMethod(@NotNull String s, @Nullable Object o)
but then tries to analyze a caller.
otherMethod() { Object o = new ...; String s = some.package.without.null.annotations.getAString(); my.mymethod(s, o); // call to check }
There might be lots of warnings of the flavor:
Warning: Can not analyze value s for NotNull; unknown nullable declaration of getAString()...
or spurious errors
"Error: s can not be used in mymethod(...), s is assumed to be nullable."
That is unless there is "don't worry I believe I know the results of that call can't be null" annotation.
@NotNull String s = ...; my.method(s, o);
then after all of the annotations are combined, the compiler won't complain.
But that seems like a lot of work.
-Paul
Dimitri Maziuk - 31 Jan 2006 17:33 GMT P.Hill sez: ... if the method is public, anyone can call it, including
>> code that has not been written yet. Compiler will have to be prescient >> to check that. ...The compiler
> would check the USE by the caller of such a method when compiling the > _caller_ time, thus no prescience is needed. And it still won't work unless you make sure *no* variable can ever be set to null: via function return, declaration without initialization, RMI, RPC, socket read, etc. Oh, and database fetch: did I mention all database types will have to be NOT NULL?
Dima
 Signature Yes, Java is so bulletproofed that to a C programmer it feels like being in a straightjacket, but it's a really comfy and warm straightjacket, and the world would be a safer place if everyone was straightjacketed most of the time. -- Mark 'Kamikaze' Hughes
David Wahler - 31 Jan 2006 19:01 GMT > P.Hill sez: > ... if the method is public, anyone can call it, including [quoted text clipped - 8 lines] > can ever be set to null: via function return, declaration > without initialization, RMI, RPC, socket read, etc. I'm not sure I'm following you. Only the variables that are declared as non-nullable would need to be checked. It's not hard for the compiler to ensure that a String variable never has an Integer value; why would it be any harder for it to check that a null value is never stored into a non-null variable?
> Oh, and > database fetch: did I mention all database types will have > to be NOT NULL? If the database table may contain nulls, the result would obviously need to be stored in a nullable variable. But there are many situations in which a null reference should never happen, and in those cases it makes sense to enforce this constraint statically.
-- David
Thomas Hawtin - 31 Jan 2006 19:41 GMT David Wahler wrote:
> I'm not sure I'm following you. Only the variables that are declared as > non-nullable would need to be checked. It's not hard for the compiler > to ensure that a String variable never has an Integer value; why would > it be any harder for it to check that a null value is never stored into > a non-null variable? It's actually a minefield.
Consider constructors. They can call (possibly virtual) methods before they are constructed. You then have an object loose with null non-null fields.
ArrayLists. You might never store nulls, and declare it as such, but the backing array is going to be full of nulls. How do you decipher that? You can still get nulls from it, if it's used in a non-thread safe manner.
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Chris Uppal - 01 Feb 2006 09:25 GMT > Consider constructors. They can call (possibly virtual) methods before > they are constructed. You then have an object loose with null non-null [quoted text clipped - 3 lines] > backing array is going to be full of nulls. How do you decipher that? > You can still get nulls from it, if it's used in a non-thread safe manner. I think that these issues could be resolved, if not quite perfectly. The basic idea would be that the programmer could tell the compiler that <such-and-such> was /intended/ not be non-null (which the compiler would just believe), or that <such-and-such> was /required/ to be non-null (which the compiler would verify by static analysis where possible and by a runtime check otherwise). The latter case would be directly analogous to casts in the current type system; the former has no direct analogue.
The case of ArrayLists is interesting. One option that the programmer would have would be to tell the compiler that it could assume that no read of the array would return a null. The compiler (perhaps naively ;-) believing that, would then allow the get(int) operation to declare that it never returned a null. Hence get() could be implemented without a run-time null-check (if that's desired). So far so good. But, as you say, there are threading issues too. I don't think that it's /necessary/ for the type system to be able to handle such cases; it would be nice if it could, but if it can't then that doesn't mean that the feature is useless.
Similarly, the fields could be declared to be non-null, and that declaration might be false if the programmer abuses constructors. Not nice, but at least /most/ accesses are subject to static confirmation. And -- if we want the extra safety -- we can use the stronger form of the assertion at the point-of-use of the field. (Compare the current situation with final variables, which can -- despite appearances -- be seen in their uninitialised state in some circumstances).
I'm not claiming that the change to the language would actually pay for itself, but I don't think we can dismiss it as valueless even though the issues you raise do dilute that value.
-- chris
Dimitri Maziuk - 01 Feb 2006 00:51 GMT David Wahler sez:
>> P.Hill sez: >> ... if the method is public, anyone can call it, including [quoted text clipped - 11 lines] > I'm not sure I'm following you. Only the variables that are declared as > non-nullable would need to be checked. Are you sure about that? -- you're compiling the *caller* and checking that it does not pass a null to public foo( @NotNull x ) declared elsewhere.
...
> If the database table may contain nulls, the result would obviously > need to be stored in a nullable variable. But there are many situations > in which a null reference should never happen, and in those cases it > makes sense to enforce this constraint statically Really? I happen to know that column i in my table is declared NOT NULL, so null reference from ResultSet.getX( i ) "should never happen". Would that be one of "those cases" where I declare public foo( @NotNull x ) for use with foo( ResultSet.getX( i ) ) so as to "enforce this constraint statically"? Or is this the case where x "obviously needs to be a nullable variable"?
Dima
 Signature ... with the exception of January and February 1900, all Microsoft application libraries counted dates the same way. -- An Interview with Joel Spolsky of JoelonSoftware
P.Hill - 01 Feb 2006 03:31 GMT > And it still won't work unless you make sure *no* variable > can ever be set to null: via function return, declaration > without initialization, RMI, RPC, socket read, etc. Oh, and > database fetch: did I mention all database types will have > to be NOT NULL? It won't work unless every way in and out, many of which you have mentioned (but many of them reduce down to whether a return value can be null), has been declared to either possibly be null or never be null, so that the much simpler analysis might be done. The proposal was to make those declarations and box the problem in.
Each of your above examples would have to declare their return values as nullable in their respective library implementations, if your statement is shown to be correct, but for example, all InputStream.read methods return something.
I believe that any such method of marking arguments and return values would then lead to the requirement to provide a way for someone to say "you can't tell from the code, but this API is guaranteed to never return null." A possibly dubious feature.
I maintain that getting all APIs to declare which possibility exists for their return values is an impossible maintenance hurdle, regardless whether the analysis could be done when presented with sufficient information.
It *might* be a feature for a new language, but it is an insurmountable impossibility for an existing language even if it is shown to be possible and useful.
-Paul
Lasse Reichstein Nielsen - 30 Jan 2006 18:54 GMT >> means the method should not return null, s should not be null and o can >> be null. The compiler could check on this.
> Like this?
> TouringMachine tm = new TouringMachine(program); > String s = null;
> if (tm.halts()){ > s = "Halts"; > } > myMethod(s, null);
> The best you will be able to get is capturing the obvious case of > calling myMethod with a direct null, but a null resulting in another > operation will not always be detected by the compiler, take my word for > it. While the compiler cannot check that the dynamic behavior of the program, it can check whether your annotations are consistent with your use.
In the above example, if "s" had been declared @Nullable, then the static check would fail, as "s" would not be a valid first argument of myMethod (it's declared to might be null). If it had been declared @NotNull, then the initial assignment would fail. A missing annotation should be interpreted as @Nullable.
I too wouldn't mind a type system that allowed nullable variables of any type as well as non-nullable ones. You could use "?" to represent it:
int? foo; foo = 42; foo = ?int;
Object? bar; bar = new Object(); bar = ?Object;
Object?? baz; baz = bar;
switch(baz) { case Object o: // use o case ?Object: // this branch taken by baz above case ??Object: // ... }
Alas, I guess it's too late for revolutionizing the Java type system like this. Gotta make my own language (hmm, is "Lava" taken :)
/L
 Signature Lasse Reichstein Nielsen - lrn@hotpop.com DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html> 'Faith without judgement merely degrades the spirit divine.'
Chris Uppal - 30 Jan 2006 19:24 GMT > I too wouldn't mind a type system that allowed nullable variables > of any type as well as non-nullable ones. You could use "?" to > represent it: >[...] > Alas, I guess it's too late for revolutionizing the Java type system > like this. Gotta make my own language (hmm, is "Lava" taken :) Too late ;-)
You may find Nice interesting:
http://nice.sourceforge.net/safety.html
-- chris
Roedy Green - 30 Jan 2006 10:02 GMT >What about attaching a "&" character next to the variable to >indicate that it cannot be null. (Just kidding :-) I ran into so many problems with programmers not documenting whether null and empty were possibilities for various variables, I came up with this scheme.
see http://mindprod.com/jgloss/void.html
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Ed - 30 Jan 2006 10:10 GMT Jacob skrev:
> A well designed API normaly doesn't accept null as parameter > values, so as a programmer you are forced to test most inputs > against null and throw an IllegalArgumentException in case it > is null. All these cases must be properly documented in the > javadoc section with an appropriate @throws tag. This sounds like overkill.
Why not just use the object and if someone has sent you a null, then you'll throw NullPointerException? Both would have the same effect: telling the client that he's made a catastrophic error and you're just not going to play with him anymore.
Admittedly, IllegalArgumentException gives more information, but I think the costs (object-testing and documentation) outweigh the benefits.
.ed
-- www.EdmundKirwan.com - Home of The Fractal Class Composition.
Roedy Green - 30 Jan 2006 12:58 GMT >Why not just use the object and if someone has sent you a null, then >you'll throw NullPointerException? Both would have the same effect: >telling the client that he's made a catastrophic error and you're just >not going to play with him anymore. the point is a formal declaration on parameters to AVOID having the error the first place. People are so sloppy about documenting whether a method can accept null or empty, or whether it will produce null or empty. That gives you a way to quickly document it, AND enforce it, much like assertions.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Andrew McDonagh - 30 Jan 2006 19:18 GMT >>Why not just use the object and if someone has sent you a null, then >>you'll throw NullPointerException? Both would have the same effect: [quoted text clipped - 6 lines] > empty. That gives you a way to quickly document it, AND enforce it, > much like assertions. trouble is, they would only stop the nulls that the compiler could detect, not those that can happen at runtime...
So why bother with a halfway house, when there's other routes available:
Throwing Exceptions (nullPointer or otherwise) Assertions Wrapping null parameters (an example is JTable in its constructor) Using NullObject Pattern etc...
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|