When null checking miserably fails
Disclaimer
Before going on I have to state that the techniques described in this article serve no practical purpose when we program Java. It is like a crossword or puzzle. It main train your brain in logical thinking, may develop your Java language knowledge or even your thinking skills. It is like a trick a magician performs. At the end you realize that nothing is what it looks like. Never do such tricks in real life programming that you may need to apply to solve this mind twister.
The Problem
I recently read an article that described the debugging case when:
if(trouble != null && !trouble.isEmpty()) { System.out.println(“fine here: ” + trouble); }else{ System.out.println(“not so fine here: ” + trouble); }
was printing out:
fine here: null
The actual bug was that the string contained “null”, a.k.a. the characters ‘n’, ‘u’, ‘l’ and ‘l’. May happen in real life especially when you concatenate strings without checking the nullity of a variable.
Then I started to think about other similar strange code and debug situation. Could I make it so that the variable is not only “null” string with these characters but really null? Seems to be crazy? Have a look at the code:
package com.javax0.blog.nullisnotnull; public class NullIsNotNull { public static void troubled(){ String trouble = new String("hallo"); Object z = trouble != null && !trouble.toString().isEmpty() ? trouble.toString() : ""; if (z == null) { System.out.println("z is really " + z + "?"); } } }
Will it ever print out the:
z is really null?
question. The fact is that you can create a Java class containing a public static void main()
so that starting that class as a Java application the sentence will be printed when main()
invokes the method troubled()
. In other words: I really invoke the method troubled()
and the solution is not that main()
prints the sentence.
In this case the variable z
is not only printed as “null” but it really is null.
Hints
The solution should not involve
- reflection
- byte code manipulation
- calling JNI
- special class loaders
- java agent
- annotation processor
These are too heavy tools. You do not need such armory for the purpose.
Hint #1
If I change the code so that the variable z
is String
it does not even compile:
If it confused you even more, then sorry. Read on!
Hint #2
In the Java language String
is an identifier and not a keyword. The Java Language Specification section 3.9 may give more information on the significance of this.
Hint #3
The method toString()
in class Object
has a return type java.lang.String
. You may want to read my article about the difference between the name, simple name and canonical name of a class. It may shed some light and increase the hit count of the article.
Hint #4
To use a class declared in the same package you need not import that package.
Solution
The solution is to create a class named String
in the same package. In that case the compiler will use this class instead of java.lang.String
. The ternary operator in the code is simple magician trick. Something to diverge your attention from the important point. The major point is that String is not java.lang.String
in the code above. If you still can not find out how to create the trick class, click on the collapsed source code block to see it in all glory:
package com.javax0.blog.nullisnotnull; class String { private java.lang.String jString; private boolean first = true; public String(java.lang.String s) { jString = s; } public boolean isEmpty() { return jString.isEmpty(); } @Override public java.lang.String toString() { if( first ){ first = false; return jString; } return null; } public static void main(java.lang.String[] args) { NullIsNotNull.troubled(); } }
Reference: | When null checking miserably fails from our JCG partner Peter Verhas at the Java Deep blog. |
Hi Peter,
I dont find any problem in compiling the code you mentioned in Hint#1.
public class TestClass {
public TestClass() {
}
public static void main(String[] args) {
String trouble = new String(“hello”);
String z = trouble != null
&& !trouble.toString().isEmpty()
? trouble.toString()
: “”;
if (z == null) {
System.out.println(“z is really ” + z +”?”);
} else {
System.out.println(“z is ” + z);
}
}
}
output: z is hello
I do not think this case will ever occur in real application.