• Post Reply Bookmark Topic Watch Topic
  • New Topic

why does java compile literal numbers added and assigned to a short, but not variable integers  RSS feed

 
Greenhorn
Posts: 7
Chrome Oracle
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
short x = 10;
short y = 3;
short z = x * y;  //DOES NOT COMPILE.  Reason says: "because x and y are automatically promoted to int.  And then setting a short variable to an int is a compile error.

However,
short a = 10 * 3;  //DOES COMPILE???  Why? exactly.  Documentation says "literals are all interpreted as integers".  So, 10 and 3 are integers.  Yet, for "some" reason that line compiles?

Any comprehensive explanation is appreciated,
donald
 
author
Marshal
Posts: 23435
138
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

donald patrick wrote:short x = 10;
short y = 3;
short z = x * y;  //DOES NOT COMPILE.  Reason says: "because x and y are automatically promoted to int.  And then setting a short variable to an int is a compile error.



This explanation isn't totally complete -- although, it is probably complete enough as an answer to a test question "why it doesn't compile?".

To be totally complete, the idea of compile time constant should be mentioned.... meaning there are additional rules based on compile time constants. This code ...

... does compile, because the right side of the assignment is a compile time constant, and can be determined to be in range of a short variable.

Henry
 
Henry Wong
author
Marshal
Posts: 23435
138
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
And this part, of course, compiles due to the compile time constant rule...

donald patrick wrote:
However,
short a = 10 * 3;  //DOES COMPILE???  Why? exactly.  Documentation says "literals are all interpreted as integers".  So, 10 and 3 are integers.  Yet, for "some" reason that line compiles?



Henry
 
Ranch Hand
Posts: 65
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I was lurking a bit and read through this post out of curiosity. This is the first time I've heard of a compile time constant. I decided to Google it and found an old post you made on this topic like 7 years ago: https://coderanch.com/t/454384/java/compile-time-constant

Not sure if it's ok for me to add a question to this post or how that works. Apologies in advance if I'm doing this wrong, but even after reading that older post and checking Google, I'm also a bit confused. Why is it a necessary thing to declare final such as with OPs code in order for it to work as long as it's within range? I can understand getting an error if you tried to assign 3.141592 to an int but the range of a short is -32,768 to 32,767 and x = 10 or y = 3 are both within that range. Again, sorry if I doing something wrong by adding a question to this.
 
Saloon Keeper
Posts: 8576
161
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
When the variables are final, the compiler can reason that they will never change, and so it's safe to inline the values wherever the variable appears in the code. Arithmetic operations on constants can be performed at compile time to further improve the code. If the resulting value is within range, it can safely be assigned.

When a variable is not final, the values can not be inlined and it becomes much much more difficult for the compiler to reason that the result of the arithmetic operation is within range of the variable. The designers probably thought it was not worth it to add this logic to the compiler.
 
Henry Wong
author
Marshal
Posts: 23435
138
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Stephan van Hulst wrote:When the variables are final, the compiler can reason that they will never change, and so it's safe to inline the values wherever the variable appears in the code. Arithmetic operations on constants can be performed at compile time to further improve the code. If the resulting value is within range, it can safely be assigned.



Interestingly, with Java 8, Java introduced the concept of "Effectively Final". I wonder if this has any effect to the compile time constant requirement?

[EDIT] Nope. The "effectively final" rule can't be used to replace the "final" declaration requirement for constant variables... so, with Java 8, it is still not a compile time constant without the "final" declaration.

Henry
 
Stephan van Hulst
Saloon Keeper
Posts: 8576
161
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Henry Wong wrote:Nope. The "effectively final" rule can't be used to replace the "final" declaration requirement for constant variables... so, with Java 8, it is still not a compile time constant without the "final" declaration.


Oh wow, that's a shame. I wonder what the reason for that is. I'm guessing it would take the compiler an extra pass to inline variables that are effectively final, but not final.
 
Marshal
Posts: 58295
178
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Effectively final (well, this is what I though) only applies to local variables or parameters used in anonymous classes or λs. It is possible to follow a local variable if it is only in scope for a few dozen lines. But what about fields? There may be hundreds of lines where they are used, and it would be too time‑consuming to verify whether they are ever reassigned.

At least I think that is what the situation is.
 
Stephan van Hulst
Saloon Keeper
Posts: 8576
161
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I'm not really talking about fields though. Apparently the compiler can already determine whether local variables are effectively final, because they can be used in lambda expressions. If they can be used in lambda expressions, then why aren't they inlined, and why aren't operators optimized away?
 
Henry Wong
author
Marshal
Posts: 23435
138
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:
At least I think that is what the situation is.



When I tested it, it was with local variables. It worked with local variables that was declared final, but not those that are effectively final.

Henry
 
Campbell Ritchie
Marshal
Posts: 58295
178
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Stephan van Hulst wrote:. . . If they can be used in lambda expressions, then why aren't they inlined, and why aren't operators optimized away?

Maybe they will in Java11. You have a good point there.
 
donald patrick
Greenhorn
Posts: 7
Chrome Oracle
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Apologies for missing my thank-you(s).  It is important to show gratitude when folks take the time to help on this forum.  Shame on me!
I'll not let this happen again.
Henry, Nikki, Stephan and Campbell - I am grateful.
It looks like I've got some testing to do to understand the "Hows and Whys" of compile time constants.
One of the trickier parts of java fundamentals is what is acceptable in the various primitive numbers.
I'd like to put my own articulation to this and similar questions, once I truly grasp it.

Again THANKS!
 
donald patrick
Greenhorn
Posts: 7
Chrome Oracle
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I've run into more issues with assignments and primitives.  So, given that I understand compile time constants... for the most part.  Now, I'm playing around with assignments using some strange, but none-the-less syntactically correct casts.
Here is my question:

byte y = (int)127 * (int)1   //compiles

short x = (long)32767 * (int)1    // does not compile... possible lossy conversion from long to short

Why would the first example not compile for similar reasons?... possible lossy conversion from int to byte?

Ultimately, I'm trying to make sure I understand all the various primitive assignments that are legal versus illegal.  To understand all the various possible situations I believe I need to understand all the various primitives to primitives, then understand all the max and mins of each (or else they won't fit), then I need to understand all the various casts that can be performed on each primitive.

 
Saloon Keeper
Posts: 19264
85
Android Eclipse IDE Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Try this:


Betcha you'll get an error (635 is too big to fit in a byte).

The key here is that you've converted both operators to bytes and done an operation whose results will also fit in a byte.

Java will upconvert if necessary, but not gratuitously. Or on other words:



Will probably fail and if you make it "+1" will almost certainly fail.

And you can tell I'm lazy because if I had my IDE up I would have already checked it.
 
Campbell Ritchie
Marshal
Posts: 58295
178
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Tim Holloway wrote:. . .

Will probably fail . . .

Why? You have three compile time constant ints constituting an expression which evaluates to an int in the range of a byte. The previous example with a long wouldn't compile because it isn't an int. Your version with + 1 evaluates to 128, outwith the range of a byte, that won't compile.

And jshell happily took your code with + 0.
 
Tim Holloway
Saloon Keeper
Posts: 19264
85
Android Eclipse IDE Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Sorry. I misread that as casting to bytes, not ints. mea culpa. 
 
Campbell Ritchie
Marshal
Posts: 58295
178
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
What difference would casting to bytes make?
 
donald patrick
Greenhorn
Posts: 7
Chrome Oracle
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I'm still not understanding.  I believe I fully understand the compile time constant parts, and I believe I fully understand the sizing requirements.  Compile time constant helps Java know for certain that the variable will not change.  As for sizing, Java will work with compile time constants and downcast them if the expression will fit in the primitive being assigned.  That's why I used (int)127, because byte will accept -128....127.  I then used long(32767), because I suspected similar logic on behalf of "Java"... ie. 32767 fits in a short, hoping it would compile since a short can accept -32768...32767.
But clearly there is something special about casting with (long).  Maybe the primitive "long" is not like the other Number primitives (byte, short, int).  But, why?
 
Bartender
Posts: 770
27
Eclipse IDE Hibernate jQuery MySQL Database Spring Tomcat Server
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

JLS 9 5.2  Assignment Contexts wrote:Assignment contexts allow the value of an expression to be assigned (§15.26) to a variable; the type of the expression must be converted to the type of the variable.

In addition, if the expression is a constant expression (§15.28) of type byte, short, char, or int:
A narrowing primitive conversion may be used if the variable is of type byte, short, or char, and the value of the constant expression is representable in the type of the variable.

Hence although 32767 fits in the range of short implicit narrowing conversion doesn't take place because of long which is omitted in the above given rule.
 
Campbell Ritchie
Marshal
Posts: 58295
178
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

donald patrick wrote:. . . Compile time constant helps Java know for certain that the variable will not change.

No, it means the compiler can calculate the value of the expression before the code is compiled. The variable that expression is assigned to might change later. In certain circumstances it means that the compiler can be confident that its value will fit into a narrower datatype, e.g. a byte.

. . . That's why I used (int)127 . . .

That is neither a narrowing conversion nor a widening conversion. It is a conversion to the same type. You can always convert an int to an int.

I then used long(32767) . . . But clearly there is something special about casting with (long). . . .

It doesn't match the rules you were shown earlier from the Java® Language Specification.

But, why?

Don't know. It is probably because the compiler is so programmed that a cast to long means you are saying you want the number not to be an int, a short, a char or a byte. Whichever it is, that is how the JLS was written and assigning a long to a smaller datatype is not permissible.

Remember that the JLS embodies the rules for the language and you have to abide by those rules.
 
donald patrick
Greenhorn
Posts: 7
Chrome Oracle
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Wow... that is a mouthful!  Much thanks to you three folks (Tim, Campbell and Ganesh).  That JLS is rather lengthy (probably why I've been afraid of it).  However, like most things complex, there usually isn't an easy answer.  There is something special about longs... they aren't special!
Thanks again for all your efforts... I really do get it!
 
With a little knowledge, a cast iron skillet is non-stick and lasts a lifetime.
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!