Java most wanted: Non-null variables

J

Jacob

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?
 
B

Bart Cremers

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
 
R

Roedy Green

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.
 
E

Ed

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
 
S

Stefan Schulz

Roedy said:
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.
 
S

Stefan Schulz

Bart said:
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

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).
 
B

Bart Cremers

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
 
R

Roedy Green

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.
 
D

David Wahler

Stefan said:
Like this?

TouringMachine tm = new TouringMachine(program);
String s = null;

if (tm.halts()){
s = "Halts";
}

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
 
D

Dimitri Maziuk

Roedy Green sez:
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
 
D

Dimitri Maziuk

David Wahler sez:
Stefan Schulz wrote: ....

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.
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
 
L

Lasse Reichstein Nielsen

Stefan Schulz said:
Bart Cremers wrote:
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
 
A

Andrew McDonagh

Roedy said:
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.

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...
 
C

Chris Uppal

Lasse said:
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
 
D

David Wahler

Dimitri said:
David Wahler sez:

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.
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.

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

P.Hill

Dimitri said:
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.
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
 
D

Dimitri Maziuk

P.Hill sez:
Dimitri Maziuk wrote:
.... if the method is public, anyone can call it, including
....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
 
D

David Wahler

Dimitri said:
P.Hill sez:
... if the method is public, anyone can call it, including

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.

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
 
T

Thomas Hawtin

David said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top