Reputation: 11131
Trying to execute this piece of code
public class Main {
public static void main(String... args) {
new Main();
}
Main() {
A a = null;
a.foo();
}
}
class A {
void foo() {}
}
I will get, obviously, a NullPointerException
since A has been initialized to null
. The code compiles because I (the coder) know that the variable a
can only be null at this location, while it (the compiler)... well, actually it knows that too, to the point that I get the warning message
Null pointer access: The variable a can only be null at this location
My question is, given that both me and it know that an exception is going to be thrown, why is my code allowed to compile? Is there any chance not to get an exception there?
Put in another way, why do I get a compiler error in someting like this
try {
throw new Exception();
int x = 0;
} catch (Exception e) {
}
and not in this
try {
if(true)throw new Exception();
int x = 0;
} catch (Exception e) {
}
Put very poorly: how does the compiler discriminates between blocking or not blocking "obvious errors"?
Upvotes: 0
Views: 114
Reputation: 3566
I May not be fully correct, But the parsers are to be blamed.I implemented a Java parser 1.5
which basically calls a compilationUnit()
.This gets started by getting the stream of the source .
The stream is tokenised (creates tokens -keywords,variables..all smaller parts).It may then make several calls- EG if the Java program has to start with a keyword, it may then look for a access specifier then.After that, the parser need to have the keyword "class".It the find other possibilities after the key word. EG. I may write public class{}
or public int i;
.If it gets the correct keyword, parsing continues else a ParseException
is thrown.
For instance I made a small change in the Parser:
private Token jj_consume_token(int kind) throws ParseException {
Token oldToken;
if ((oldToken = token).next != null) {
token = token.next;
} else {
token = token.next = token_source.getNextToken();
}
jj_ntk = -1;
if (token.kind == kind) {
jj_gen++;
if (++jj_gc > 100) {
jj_gc = 0;
for (int i = 0; i < jj_2_rtns.length; i++) {
JJCalls c = jj_2_rtns[i];
while (c != null) {
if (c.gen < jj_gen) {
c.first = null;
}
c = c.next;
}
}
}
return token;
}
jj_kind = kind;
throw generateParseException();
}
}
To:
private Token jj_consume_token(int kind) throws ParseException {
try{
Token oldToken;
if ((oldToken = token).next != null) {
token = token.next;
} else {
token = token.next = token_source.getNextToken();
}
jj_ntk = -1;
if (token.kind == kind) {
jj_gen++;
if (++jj_gc > 100) {
jj_gc = 0;
for (int i = 0; i < jj_2_rtns.length; i++) {
JJCalls c = jj_2_rtns[i];
while (c != null) {
if (c.gen < jj_gen) {
c.first = null;
}
c = c.next;
}
}
}
return token;
}
token = oldToken;
jj_kind = kind;
throw generateParseException();
} catch (ParseException ex) {
recover(ex,SEMICOLON);
return token;
}
}
Recovery block looks like:
void recover(ParseException ex, int recoveryPoint) {
syntaxErrors.add(ex);
Token t;
do {
t = getNextToken();
} while (t.kind != EOF && t.kind != recoveryPoint);
}
So what I just did? I recovered the exception thrown by the parser even if there is no method or just statements.EG
class A{
Date d;
System.out.print("No exception");
}
I don't get error now.I made changes.So, if you want your parser to report an Exception in such cases, you can implement your parser :) hope you get it now.
Upvotes: 1
Reputation: 3566
From Java spec
4.12.2
A variable of a class type T can hold a null reference or a reference to an instance of class T or of any class that is a subclass of T.
Also, For instance consider :
while(true)
{
//stay blank
}
This gives error in my NetBeans that following line is unreachable.
Again,
while(true)
{
if(false)break;
}
Now what you expect?Same thing? Its not.It goes into infinite loop. Similarly, you should be able to catch NPE in your code.It is allowed.And so there might exist many combinations for different type of such ambiguity.Which is rather difficult to choose & restrict.
Upvotes: 1
Reputation: 15212
My question is, given that both me and it know that an exception is going to be thrown, why is my code allowed to compile
null
is not a compile time constant. The compiler does not know at compile time that A a = null;
evaluates to null
.
Further reading :
Why isn't null a compile time constant
Upvotes: 2
Reputation: 100269
It's allowed to compile as Java Language Specification does not forbid it. Who knows, probably you want to catch this NullPointerException
in the caller. That would be not the best code design, but it's allowed by JLS.
Note that Eclipse compiler can be configured to display an error at this point. By default it issues a warning:
$ java -jar org.eclipse.jdt.core_3.10.2.v20150120-1634.jar -source 1.7 -cp rt.jar Main.java
----------
1. WARNING in L:\Lan\Projects\JavaTest\EcjTest\src\Main.java (at line 8)
a.foo();
^
Null pointer access: The variable a can only be null at this location
----------
1 problem (1 warning)
Also static code analyzers like FindBugs will trigger a warning on this code.
The question why it is not forbidden by JLS would be opinion based. However note that such feature cannot be added now, because this may break the backwards compatibility. I believe, there's an existing code which may actually rely on such behavior.
Upvotes: 3