• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

Is there a rule of thumb for what the compiler will catch?

 
Greenhorn
Posts: 8
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Very often as I am doing the practices exercises for the exam, I will know what the problem is in the code but do not know whether the result will be a compiler or a runtime error.  Is there a rule of thumb that I can apply as to what the compiler will do in a certain situation?

There are bound to be at least 1 or 2 questions on the exam where option E is "will not compile" and option F is "runtime exception".  I would like to be able to choose the correct answer.

Thanks in advance.

John
 
author & internet detective
Posts: 41860
908
Eclipse IDE VI Editor Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
John,
The compiler will catch syntax and whether a checked exception can be thrown. Vs a runtime error which means an exception is thrown.
 
John Cunningham
Greenhorn
Posts: 8
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks, Jeanne.  That helps some.

But, if you don't mind, I will give an example.  What if a have a stream with a source and at possibly some intermediate methods.  Does the compiler know that there needs to be one and only one terminal method?  

1) What if the there's no terminal methods - will the program do nothing, not compile or give an exception at runtime?
2) What if there are 2 terminal methods - will the program do nothing, not compile or give an exception at runtime?
3) What if you start a new stream with the same stream reference, will the compiler catch that one?

I will experiment myself and get back to you tomorrow.

Thanks again.

John
 
Jeanne Boyarsky
author & internet detective
Posts: 41860
908
Eclipse IDE VI Editor Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The compiler will only catch if there is a syntax error in your example.

1) What if the there's no terminal methods - will the program do nothing, not compile or give an exception at runtime?


That means this will compile although it will not print what you want.


But this will not because it returns a stream and not an int.


2) What if there are 2 terminal methods - will the program do nothing, not compile or give an exception at runtime?


Not compile. Because a terminal method doesn't return a stream so the compiler knows something is wrong when you try to add a second one.

3) What if you start a new stream with the same stream reference, will the compiler catch that one?


No. That is a runtime exception.
 
Bartender
Posts: 1737
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
ALERT: This has been posted in the wrong forum, but Jeanne saw it anyway.

The answer came late at night and I think it missed some stuff, however, so I am adding a bunch to the answer.

I thought most of the tricky "will this compile?" questions, including in Jeanne's mock exams, often involve the presence or absence of final and compile time determinism making things compile or not.  What the heck does THAT mean?

The compiler doesn't do any analysis of the values of any of your variables in terms of unreachable code or casting/assignment errors.

Unless they are "constant expressions" that are known at compile time, in which case it definitely does.

For instance, these won't compile without a cast:

Both return Type Mismatch compile time errors.

This compiles fine:


So you might expect this to as well (well, I'd expect it), but no dice:


In a similar vein, this compiles just fine:


But this won't compile (Unreachable Code):


So now you know that this will get you an "Unreachable Code" compilation failure, right?

It won't!!

What about this?


This however, will NOT compile:

It declares that the println() in the while loop is unreachable.

Whereas if we change it a bit to this, it declares that the println() AFTER the while loop is unreachable:


Without the magic word final there, it compiles just fine either way.

This won't compile:

(Case expressions must be constant expressions)
but slap a final in front of the notSyntax and assign a compile time constant to it and you are good to go.

I'd say lastly, as I probably forgot other things, but I remember this one:
This will not compile:


Unreachable catch block for CloneNotSupportedException. This exception is never thrown from the try statement body SoEasy.java /easyconsoleIO/src/easyconsoleIO line 60 Java
When it does analysis to make sure you handled all Checked Exceptions properly, for some reason it ensures that your code can actually possibly throw any checked ones you claim to catch.

I don't consider any of these cases to be "just syntax", and I learned a few of these from Jeanne and Scott's books!

As to whether you can expect any of these to be in scope for the current 819, I'd defer to the two of them, who I myself look to for such answers.
But I can tell you a heck of a lot of older mock exams are filled with these "Will this compile or not?" land mines.


 
Jesse Silverman
Bartender
Posts: 1737
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The last example may be redundant.
When I learned about Checked Exceptions, I was surprised that not only does it check to make sure you didn't miss the handling of any that it thinks CAN occur, it also barfs if you try to catch one that CAN'T HAPPEN.
I am unsure, but I think that means that if someone changes a method to no longer declare it throws one, your code could break the next time it compiles.
That is why Checked Exceptions are considered part of the signature of methods.
 
Sheriff
Posts: 17644
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
There's a section in the JLS that discusses unreachable statements. The end of that section explains why if-statements are treated differently by the compiler.

https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.21
 
Jesse Silverman
Bartender
Posts: 1737
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks Junilu!

I think that is separate from syntax tho trying to catch something it determines won't be thrown counts as a specific example of "unreachable statements", which at least in the past, one needed to look out for.
 
Master Rancher
Posts: 4806
72
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jesse Silverman wrote:That is why Checked Exceptions are considered part of the signature of methods.


Well, not according to the JLS: 8.4.2. Method Signature.  The signature consists of the method name, the type parameters, and the formal parameter types.  No throws clause, and no return type.  Paradoxically, both the throws clause and the return type are part of what the Java Virtual Machine Specification refers to as the "signature".  But how the JVM defines things is an implementation detail, from the Java language perspective.  If you're trying to decide whether you're looking at an override or an overload, for example, you need to follow the JLS definition of signature, and ignore the return type and throws clause.  Then you immediately remember them again when you try to decide if it's a legal override.  
 
Jesse Silverman
Bartender
Posts: 1737
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Junilu Lacar wrote:There's a section in the JLS that discusses unreachable statements. The end of that section explains why if-statements are treated differently by the compiler.

https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.21



I had heard people allege that the reason that if / else don't behave as one would expect with "reachability" compile failures was indeed conditional compilation.

I had thought that was speculation or hearsay, but now see they just RTFM.

Cool.
 
Jesse Silverman
Bartender
Posts: 1737
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Mike Simmons wrote:

Jesse Silverman wrote:That is why Checked Exceptions are considered part of the signature of methods.


Well, not according to the JLS: 8.4.2. Method Signature.  The signature consists of the method name, the type parameters, and the formal parameter types.  No throws clause, and no return type.  Paradoxically, both the throws clause and the return type are part of what the Java Virtual Machine Specification refers to as the "signature".  But how the JVM defines things is an implementation detail, from the Java language perspective.  If you're trying to decide whether you're looking at an override or an overload, for example, you need to follow the JLS definition of signature, and ignore the return type and throws clause.  Then you immediately remember them again when you try to decide if it's a legal override.  



I don't think I ever thought about signatures definition enough to see how it makes sense in a "We don't count 1 as a prime number" sort of way.

For override, for example, the signature must match *exactly*.
There are also rules (Co-Variance) about return type and may throw sub-classes of checked exceptions declared in super-class throws clause, and any unchecked exceptions it feels like...
Those aren't required to be exact matches so would totally screw up saying "signatures must match" AND (stuff about return type and exceptions).

Same deal with overloads, MUST have different signatures but same method name....exact statement of the rules for legal overloading may be a little weird with generics and type erasure...but you wouldn't be able to say "must have different signatures" and be done with it, and method overload resolution would be impossible if they included throws clause or return type in the signature definition -- we don't have information at the caller site to pick between those!!

OK, that all makes sense, the more proper statement is "Yeah, don't forget you can fail compile by either failing to catch or "throws" a checked exception, or by trying to catch one that isn't possible.  Putting it in the throws clause won't break even if the code never raises it.
 
Saloon Keeper
Posts: 15510
363
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

John Cunningham wrote:But, if you don't mind, I will give an example.  What if a have a stream with a source and at possibly some intermediate methods.  Does the compiler know that there needs to be one and only one terminal method?


John, here's a rule of thumb for you.

If you look at the lines of code and must evaluate them (execute them in your head) to figure out if they're correct, then it's likely a runtime exception.

If you don't need to calculate the results of a statement in your head, but something else is wrong (a typo, a variable is used but isn't declared, that sort of stuff) then it's likely a compiler error.

There's nothing magical about terminal Stream methods. They're just regular methods like any other. The only reason we know they're terminal is because the documentation says they're terminal. If you hadn't read the Javadocs, calling two different terminal methods on the same stream would just look like valid Java code. You need to evaluate the two different method calls to find out that it will throw a runtime exception.
 
Jesse Silverman
Bartender
Posts: 1737
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

There's nothing magical about terminal Stream methods. They're just regular methods like any other. The only reason we know they're terminal is because the documentation says they're terminal. If you hadn't read the Javadocs, calling two different terminal methods on the same stream would just look like valid Java code. You need to evaluate the two different method calls to find out that it will throw a runtime exception.



I need to review this huge chapter, in Jeanne and Scott's book, but is that consistent with what she says here tho?

Jeanne Boyarsky wrote:The compiler will only catch if there is a syntax error in your example.

...

2) What if there are 2 terminal methods - will the program do nothing, not compile or give an exception at runtime?


Not compile. Because a terminal method doesn't return a stream so the compiler knows something is wrong when you try to add a second one.



 
Stephan van Hulst
Saloon Keeper
Posts: 15510
363
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I assumed John asked about such a case:
 
Jesse Silverman
Bartender
Posts: 1737
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Ah, yes.  I normally use the chaining idiom pretty heavily when working with streams, so something like this will fail to compile:

jshell> List<Integer> li = List.of(2, 4, 6, 8)
li ==> [2, 4, 6, 8]

jshell> li.stream().count().findany()
|  Error:
|  long cannot be dereferenced
|  li.stream().count().findany()
|  ^-------------------------^


So basically, if something can be determined to be wrong at compile time,l (and not everything that superficially seems to be in that case is)  it will be a compiler error.
If not, runtime exception.

As an aside, boy is JShell handy when trying out most unfamiliar areas of Java quickly.  Seriously, a lot.
 
Junilu Lacar
Sheriff
Posts: 17644
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jesse Silverman wrote:So basically, if something can be determined to be wrong at compile time,l (and not everything that superficially seems to be in that case is)  it will be a compiler error.
If not, runtime exception.


More or less, yes. I might tweak that a little bit to be "if something can be determined to be wrong based on information that can be gleaned from type definitions, syntax rules, and static analysis at compile time, it will be a compiler error."

Determining if a line of code is unreachable is an example of what I mean by "static analysis."
 
Jesse Silverman
Bartender
Posts: 1737
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Junilu Lacar wrote:

Jesse Silverman wrote:So basically, if something can be determined to be wrong at compile time,l (and not everything that superficially seems to be in that case is)  it will be a compiler error.
If not, runtime exception.


More or less, yes. I might tweak that a little bit to be "if something can be determined to be wrong based on information that can be gleaned from type definitions, syntax rules, and static analysis at compile time, it will be a compiler error."

Determining if a line of code is unreachable is an example of what I mean by "static analysis."



With my normal mix of trepidation and daring in contradicting anything you ever say, I am going to go out on a limb and say that for all languages, the boundaries of "static analysis" have expanded as far beyond what I once considered their limits as almost any other area of computing.

I spent a lot of time with Coverity and Klocwork, on a code base composed of a heady mix of languages including C, C++, C# and Java.
The things that they were able to find went beyond what I originally expected by a factor of between 10 and 100.
They all have the ability to play "What If" to an astounding degree.  They have a fairly low rate of false positives, tho some of the things they do find are possible but exceedingly unlikely, in that they would require *just* the perfect storm of data or timing coincidences to manifest in real life.

I only looked briefly at this list, but probably most/all of them go far beyond what the Java compiler still considers "reasonable static analysis" to find all kind of latent problems:
https://www.softwaretestinghelp.com/tools/top-40-static-code-analysis-tools/

Even the free static analysis tools such as FindBugs and -- here I realize my awareness of competitive free static analyzers in the Java space are somewhat weak -- call themselves static analyzers and find many, many problems in code that would compile just fine.  Of your trinity of "type definitions", "syntax rules" and "static analysis" the first two are the same, but they stretch the boundaries of "static analysis" to play all sorts of games with "what will happen in this call chain if this possible data and timing coincidences should occur?"

It might be nice (if inappropriate to ask for) to have this sort of analysis as a built-in option to our compilers, but there are two reasons I think it would be unreasonable.

1. Runs on code bases that might take an hour to compile could take ten, twenty or forty hours as the algorithms go wild with "What if?" and
2. the things that are found require detailed explanations in order to get developers to even believe they are problems.
At least with C/C++ it wasn't uncommon to spend an hour going thru their walkthrus again and again with the developer who owned the code in question, to finally realize that the problem they found was probably crashing once a week somewhere on the planet for the last ten years, and explained some undeterminable, but almost certainly non-zero percentage of "never explained" crashes in our software.  There would be no reasonable way to present such detailed analysis in a compilation failure or warning.

That said, having worked a lot in Python lately, every time I sat in the debugger carefully stepping thru only to realize the problem was something that would never have compiled in the equivalent Java code, I appreciate what the compiler is doing for us.  But it only wades in the very shallowest end of the pool of "Static Analysis".  To the extent that it does no "Dynamic Analysis" all of the compiler warnings and errors are derived from "Static Analysis" but of the older, more limited sort that I used to think was the only kind practically possible in real life.
 
Mike Simmons
Master Rancher
Posts: 4806
72
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Junilu Lacar wrote:I might tweak that a little bit to be "if something can be determined to be wrong based on information that can be gleaned from type definitions, syntax rules, and static analysis at compile time, it will be a compiler error"


However, "static analysis" could include many things that are not compiler errors, because the designers of Java didn't want different compilers to behave too differently - they regulated exactly what types of problems would result in a compiler error, and which would not.  So for example:

This will compile just fine, because the type of s is an Object, and the compiler knows that an Object has a hashCode() method, so you can write code to invoke that method.  Now we know that this code will not run successfully; it will obviously throw an exception because s is null.  And really, we would prefer if the compiler prevented us from committing such a stupid error.  Unfortunately, the compiler is not allowed to not compile here - it's required to produce bytecode that will run and throw a NullPointerException here.  Because while in this example it's exceedingly simple to spot the error that will occur, more generally, it may not be obvious.  And Java's creators didn't want to create a situation where some smart compilers will refuse to compile code, and other less smart compilers would compile it - or vice versa.  Instead, the idea is that any valid Java compiler should produce roughly the same result.  So they basically decided that compilers would not take note of the actual values of variables.  Instead the compiler only pays attention to the type of the variable.  And even then, not the actual type of the object referenced by the variable, but the declared type of the variable.

On the other hand, your IDE may well do some static analysis of its own.  And the IDE is allowed to give you any warnings it wants to.  So my IntelliJ tells me "Method invocation 'hashCode' will produce 'NullPointerException'" for the above code - cool!  That's useful info.  But if I decide to run it anyway, I can still compile and run, and get a NullPointerException.

 
Junilu Lacar
Sheriff
Posts: 17644
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Jesse Silverman wrote:With my normal mix of trepidation and daring in contradicting anything you ever say


I'd say you give me too much credit. I'm known to make mistakes, you know. Just ask my wife.

1. Runs on code bases that might take an hour to compile could take ten, twenty or forty hours as the algorithms go wild with "What if?" and


That's probably the main consideration for how deep/shallow the static analysis done by the compiler would be. There's a balance that needs to be struck between fast compiles and avoiding bugs and from the compiler designer's point of view, the former probably outweighs the latter consideration.
 
Jesse Silverman
Bartender
Posts: 1737
63
Eclipse IDE Postgres Database C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Mike Simmons wrote:

Junilu Lacar wrote:I might tweak that a little bit to be "if something can be determined to be wrong based on information that can be gleaned from type definitions, syntax rules, and static analysis at compile time, it will be a compiler error"


However, "static analysis" could include many things that are not compiler errors, because the designers of Java didn't want different compilers to behave too differently - they regulated exactly what types of problems would result in a compiler error, and which would not.  So for example...
 And Java's creators didn't want to create a situation where some smart compilers will refuse to compile code, and other less smart compilers would compile it - or vice versa.  Instead, the idea is that any valid Java compiler should produce roughly the same result.  So they basically decided that compilers would not take note of the actual values of variables.  Instead the compiler only pays attention to the type of the variable.  And even then, not the actual type of the object referenced by the variable, but the declared type of the variable.



Wow, do I deeply appreciate this answer, and more deeply than ever appreciate the behavior of the Java compiler!

Java's developers came out of much the same world that I did.  I was supporting millions of lines of code across 20 different platforms, doing all the builds.
Did we ever have situations where developers on one platform would say that their code was fine, and point to their own clean compiles and between 5 and 16 of the other 20 and all but tell us "Nope, I did nothing wrong!" ?? Only at least a dozen times a week.  It got to the point that I'd carry around a paper copy of Harbison and Steele (the latter of whom works on the JLS) to lecture them like I was serving a warrant or something.  Now, as part of the team that had to make sure our code actually worked, I appreciated the most careful compilers.  I guess it was the nature of C that it was actually very common to screw source up in a way that would be caught by between 5 and 15 of 20 different compilers, heavily dependent on options.  Some errors would be noticed in DEBUG settings (well, more in runtime but sometimes compile time too), many others would be noticed on some compilers only when certain optimizations were turned on...if all 20 compilers liked it you were probably good until you accessed freed memory and crashed.

So I can see that limiting what errors the compilers can emit in what cases is actually a feature, whereas I was thinking of it as a bug.

I see sections 9.6.4.5 thru 9.6.4.7 talk a lot about warnings.  I also know that a lot of things I wish were errors are instead warnings because first Sun, then Oracle didn't want to break code that had been compiling and running fine since 1995.  Once that decision is made and stuck to, there's nothing to talk about -- it is up to the owners of the code to treat them as if they were errors to be fixed...
 
Mike Simmons
Master Rancher
Posts: 4806
72
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
One of the driving principles of Java's development, now mostly taken for granted except when it's violated, was "write once, run anywhere".  This was also applied to "compile anywhere".
 
John Cunningham
Greenhorn
Posts: 8
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks, everybody, for your responses.  I learned a lot.  Not so black and white as I thought(particularly the unreachable code).  When I get the chance, I will delve into the different replies and test them myself.  

It doesn't sound like there's any universal rule of thumb that can be applied in all cases, but I will try to put Stephan's suggestion to the test with some practice exercises.

Thanks, again!
 
Jesse Silverman
Bartender
Posts: 1737
63
Eclipse IDE Postgres Database C++ Java
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I find my initial anxiety about failing to distinguish between compile errors and runtime problems in JShell were a bit overblown.

As a result, JShell turns out to be a great environment to try a zillion things and see how they work or fail.

Of course, the IDE's mostly run static analysis on your code before you even finish typing it, but occasionally may depart from the "Strict Java Rules" we were referring to.

Cheers
 
Don't get me started about those stupid light bulbs.
reply
    Bookmark Topic Watch Topic
  • New Topic