Numeric promotion rules

Kamil Hlubek
Ranch Hand
Posts: 49
2
Hi guys,

I have worked with primitive data types and I noticed also, that if you write this:

126 is printed.

But when you use a binary operator it is clear, that a compiler error occurs as the following demonstrates:

My question is now:

Why the addition in the first example do not end with a compilation error although an binary operator is used.

I mention the binary operators very strongly, because of a promotion rule in Jeanne Boyarsky's book, which is:

"Smaller data types, namely byte short, and char, are first promoted to int any time they're used with Java binary arithmetic operator, even neither of the operands is int. "

It would be nice if somebody could me explain the differences between the two examples.
( I am pretty sure, that it is something about the literals and non-literals, but I find this statement from this book is generally very abstract,
because every integer is implicity an int ( This should be wrong if you look at the first example, but I want to understand it completely so I write this here down ).

( I had positive experiences with that somebody put a link as a response so I would be happy with it,too )

Yours sincerely,

Kamil

Henry Wong
author
Marshal
Posts: 22124
88
First, here is a topic discussion regarding compile time constants (http://www.coderanch.com/t/454384/java/java/compile-time-constant). Numeric literals are compile time constants, and hence, follow rules regarding it.

Second, read section 5.2 of the JLS, which explains the conversions needed for assignments (see https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.2). Take care to examine the section on compile time constants.

Kamil Hlubek wrote:
Why the addition in the first example do not end with a compilation error although an binary operator is used.

The expression "125 + 1" is actually a compile time constant. The arithmetic is done at compile time. This code is no different than assigning the literal 126 to the variable. And of course, the compile time constant rule regarding assignments allow for the assignment.

Kamil Hlubek wrote:
It would be nice if somebody could me explain the differences between the two examples.
( I am pretty sure, that it is something about the literals and non-literals, but I find this statement from this book is generally very abstract,
because every integer is implicity an int ( This should be wrong if you look at the first example, but I want to understand it completely so I write this here down ).

In the second case, it isn't a compile time constant. The program has to evaluate the expression "b1 +1" at runtime. And because of this, the compile time constant section of the 5.2 rule does not apply... so, you need to explicitly cast the int to a byte.

Henry

Roel De Nijs
Sheriff
Posts: 10666
144
• 1
Let's see if you completely understand Henry's great explanation. It's time for a pop-quiz

Which of the following lines won't compile? And why?

Best of luck!
Kind regards,
Roel

Kamil Hlubek
Ranch Hand
Posts: 49
2
• 1

The first line ( Line 1 ) is a compile time constant variable, because it is a final variable, is assigned at the same time as the declaration of it, has a primitive data type and it is initialized with a compile-time constant value. ( This fact is important for the following code. )

Line 2 do NOT compile, because of the use of a non-compile-time constant variable ( "MAX_SIZE" ). The reason why it is not a compile-time contant value is, that it is not a final variable.

Line 3 compiles without a problem, because, as previously explained, "b1" is a compile-time constant variable and 15 is a literal, which is a component for compile-time constant expressions. The result is a compile-time constant expression denoting a value of the primitive data type int.

Line 4 do not compile, because of the two operands. The first operator is not a compile-time constant variable, because it is not instantiated at the same time as the declaration . The second operator is not a compile-time constant variable, too, but not because of the same reason. The reason here is that the variable "MAX_SIZE" is not declared as a final variable.
BUT this compilation error would not occur, because a static method can not use a non-static variable, ehich is here size.

Line 5 does not compile, because a method invocation is not a compile-time constant value, so the compiler casts the returned byte-value to an int value, which result in a compilation error, because of the possible loss of precision ( int to byte does not work! ).
-> This line compiles, but why and why line 6 suddenly do not compile?

On line 6, b(four) is not a final variable so it is not a compile-time constant variable and so the problem is the same, as on line 5.

Line 7 compiles, because of the reasons I already mentioned previously ( b1 is a compile-time constant variable and the literals are compile-time constants.

Line 8 compiles, because of the same reason, as line 7.

Line 9 compiles. ( I do not explain it anymore, because I have already written it very often and the variables are still the same )

Line 10 do not compile , because Byte -> no compile-time constant value .

Henry Wong
author
Marshal
Posts: 22124
88
• 1
Kamil Hlubek wrote:
Line 5 does not compile, because a method invocation is not a compile-time constant value, so the compiler casts the returned byte-value to an int value, which result in a compilation error, because of the possible loss of precision ( int to byte does not work! ).
-> This line compiles, but why and why line 6 suddenly do not compile?

The literal() method returns a byte value, and that byte value is simply being assigned to a byte variable. There are *no* other operations happening -- it is simply an assignment (byte to byte). Why should it lose precision? Meaning why should it not compile?

The followup questions is... why does this compile? As a literal int is being returned by a method that is expected to return a byte.

In section 14.17 of the JLS, that defines the return statement ... https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.17 ... it states that the return value must be assignable as defined by section 5.2 (which we already discussed), and this is where the compile time constant rule is applied.

Henry

PS... BTW, a cow for the test, and a cow for willing to do the test (with detailed explanations) ...

Kamil Hlubek
Ranch Hand
Posts: 49
2
The literal() method returns a byte value, and that byte value is simply being assigned to a byte variable. There are *no* other operations happening -- it is simply an assignment (byte to byte). Why should it lose precision? Meaning why should it not compile?

This was really a careless mistake. Apparently I worked to long with it and did not focused on it. Sorry for that.

Henry Wong
author
Marshal
Posts: 22124
88
Kamil Hlubek wrote:
This was really a careless mistake. Apparently I worked to long with it and did not focused on it. Sorry for that.

Nothing to be sorry about. You did the test thoroughly -- and earned a cow...

Henry

Roel De Nijs
Sheriff
Posts: 10666
144
• 1
Kamil Hlubek wrote:The first line ( Line 1 ) is a compile time constant variable, because it is a final variable, is assigned at the same time as the declaration of it, has a primitive data type and it is initialized with a compile-time constant value.

Spot-on!

Kamil Hlubek wrote:Line 2 do NOT compile, because of the use of a non-compile-time constant variable ( "MAX_SIZE" ). The reason why it is not a compile-time contant value is, that it is not a final variable.

Correct! Here the code tried to trick you by the notation of the variable MAX_SIZE. All uppercase with words separated by an underscore suggests MAX_SIZE is a constant, but it clearly isn't (final is missing).

Kamil Hlubek wrote:Line 3 compiles without a problem, because, as previously explained, "b1" is a compile-time constant variable and 15 is a literal, which is a component for compile-time constant expressions. The result is a compile-time constant expression denoting a value of the primitive data type int.

True! Because b1 is a compile-time constant and 15 is a literal, the compiler knows the sum is 115 which is in the range of a byte. So the result can be assigned to b3 without an explicit cast.

Kamil Hlubek wrote:Line 4 do not compile, because of the two operands. The first operator is not a compile-time constant variable, because it is not instantiated at the same time as the declaration . The second operator is not a compile-time constant variable, too, but not because of the same reason. The reason here is that the variable "MAX_SIZE" is not declared as a final variable.
BUT this compilation error would not occur, because a static method can not use a non-static variable, ehich is here size.

Correct again! Although I have to make one small remark: the first operator is not initialized at the same time as the declaration. A class is instantiated to create a new object, a primitive variable is initialized (not instantiated).

Kamil Hlubek wrote:Line 5 does not compile, because a method invocation is not a compile-time constant value, so the compiler casts the returned byte-value to an int value, which result in a compilation error, because of the possible loss of precision ( int to byte does not work! ).
-> This line compiles, but why and why line 6 suddenly do not compile?

As already mentioned (and explained) by Henry, line 5 compiles successfully because you assign a byte (the return type of a method) to another byte variable (b4).

Kamil Hlubek wrote:On line 6, b(four) is not a final variable so it is not a compile-time constant variable and so the problem is the same, as on line 5.

Indeed! Although b4 is a final variable, it's not a compile-time constant. So the sum will result in an int and without an explicit cast this value can't be assigned to a byte variable.

Kamil Hlubek wrote:Line 7 compiles, because of the reasons I already mentioned previously ( b1 is a compile-time constant variable and the literals are compile-time constants.
Line 8 compiles, because of the same reason, as line 7.
Line 9 compiles. ( I do not explain it anymore, because I have already written it very often and the variables are still the same )

You are spot-on on all three! But in your explanation you have forgotten one important condition: for every expression at the right side, the result fits in the range of the primitive data type at the left. If that was not the case, that line would not compile although you have compile-time constants and literals. For example this line would not compileThe result of b1 * s2 (100 * 10000) is 1000000 which is clearly outside the range of a char.

Kamil Hlubek wrote:Line 10 do not compile , because Byte -> no compile-time constant value .

And you are spot-on on the last one as well! byte and Byte is definitely not the same. A reference variable of a primitive wrapper class can never be a compile-time constant. Having an eye for detail on the exam is very important: the case of a letter can make the difference between "Compiles successfully" or "Compilation fails". And you did a great job with this exercise!

Hope it helps!
Kind regards,
Roel