This question already has answers here:
Why can not I add two bytes and get an int and I can add two final bytes get a byte?
(3 answers)
Why does Java require an explicit cast on a final variable if it was copied from an array?
(4 answers)
Primitive type 'short' - casting in Java
(11 answers)
Java char to byte casting
(3 answers)
Closed 4 years ago.
§5.1.2 and §5.6.2 do not mention how numeric promotion and widening work for constants.
The following gives an error as expected:
short a = 2;
short b = 3;
short s = a + b; // error: incompatible types: possible lossy conversion from int to short
But if they are declared final, it compiles without error:
final short a = 2;
final short b = 3;
short s = a + b; // no error
Why is that? And which section of the specs explains that?
My guess is that they are compile time constants and therefore treated as integers.
Promotion necessarily must be done by the compiler because there are no JVM instructions for addition of shorts.
However, the compiler is able to narrow the resulting integer sum to a short because:
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 type of the
variable is byte, short, or char, and the value of the constant
expression is representable in the type of the variable.
as described in the JLS.
Adding final determines whether or not it's a constant expression.
Related
If you declare variables of type byte or short and attempt to perform arithmetic operations on these, you receive the error "Type mismatch: cannot convert int to short" (or correspondingly "Type mismatch: cannot convert int to byte").
byte a = 23;
byte b = 34;
byte c = a + b;
In this example, the compile error is on the third line.
Although the arithmetic operators are defined to operate on any numeric type, according the Java language specification (5.6.2 Binary Numeric Promotion), operands of type byte and short are automatically promoted to int before being handed to the operators.
To perform arithmetic operations on variables of type byte or short, you must enclose the expression in parentheses (inside of which operations will be carried out as type int), and then cast the result back to the desired type.
byte a = 23;
byte b = 34;
byte c = (byte) (a + b);
Here's a follow-on question to the real Java gurus: why? The types byte and short are perfectly fine numeric types. Why does Java not allow direct arithmetic operations on these types? (The answer is not "loss of precision", as there is no apparent reason to convert to int in the first place.)
Update: jrudolph suggests that this behavior is based on the operations available in the JVM, specifically, that only full- and double-word operators are implemented. Hence, to operator on bytes and shorts, they must be converted to int.
The answer to your follow-up question is here:
operands of type byte and short are automatically promoted to int before being handed to the operators
So, in your example, a and b are both converted to an int before being handed to the + operator. The result of adding two ints together is also an int. Trying to then assign that int to a byte value causes the error because there is a potential loss of precision. By explicitly casting the result you are telling the compiler "I know what I am doing".
I think, the matter is, that the JVM supports only two types of stack values: word sized and double word sized.
Then they probably decided that they would need only one operation that works on word sized integers on the stack. So there's only iadd, imul and so on at bytecode level (and no operators for bytes and shorts).
So you get an int value as the result of these operations which Java can't safely convert back to the smaller byte and short data types. So they force you to cast to narrow the value back down to byte/short.
But in the end you are right: This behaviour is not consistent to the behaviour of ints, for example. You can without problem add two ints and get no error if the result overflows.
The Java language always promotes arguments of arithmetic operators to int, long, float or double. So take the expression:
a + b
where a and b are of type byte. This is shorthand for:
(int)a + (int)b
This expression is of type int. It clearly makes sense to give an error when assigning an int value to a byte variable.
Why would the language be defined in this way? Suppose a was 60 and b was 70, then a+b is -126 - integer overflow. As part of a more complicated expression that was expected to result in an int, this may become a difficult bug. Restrict use of byte and short to array storage, constants for file formats/network protocols and puzzlers.
There is an interesting recording from JavaPolis 2007. James Gosling is giving an example about how complicated unsigned arithmetic is (and why it isn't in Java). Josh Bloch points out that his example gives the wrong example under normal signed arithmetic too. For understandable arithmetic, we need arbitrary precision.
In Java Language Specification (5.6.2 Binary Numeric Promotion):
1 If any expression is of type double, then the promoted type is double, and other expressions that are not of type double undergo widening primitive conversion to double.
2 Otherwise, if any expression is of type float, then the promoted type is float, and other expressions that are not of type float undergo widening primitive conversion to float.
3 Otherwise, if any expression is of type long, then the promoted type is long, and other expressions that are not of type long undergo widening primitive conversion to long.
4 Otherwise, none of the expressions are of type double, float, or long. In this case, the promoted type is int, and any expressions that are not of type int undergo widening primitive conversion to int.
Your code belongs to case 4. variables a and b are both converted to an int before being handed to the + operator. The result of + operation is also of type int not byte
This question already has answers here:
Promotion in Java?
(5 answers)
Closed 9 years ago.
I have three short variables.
When I add two together and assign the result to the third, eclipse tells me that I need to cast it to a short !
short sFirst, sSecond, sThird;
sFirst = 10;
sSecond = 20;
sThird = sFirst + sSecond;
Hovever, when I do a simple assignment followed by an incremental assignment, all is fine.
short sFirst, sSecond, sThird;
sFirst = 10;
sSecond = 20;
sThird = sFirst;
sThird += sSecond;
Why is this ?
The JLS (§15.8.2) says this:
"The binary + operator performs addition when applied to two operands of numeric type, producing the sum of the operands."
"Binary numeric promotion is performed on the operands (§5.6.2)."
That means that the operands of your expression are converted to int. So the addition will add an int to an int.
"The type of an additive expression on numeric operands is the promoted type of its operands."
In your case, int.
I won't speculate as to why it is done this way. However, it is no accident. If you look at the bytecode instruction set as defined in the JVM spec, you will see that there are arithmetic instructions for int, long, float and double ... but NOT for the smaller integer types.
This behavior is precisely specified in the Java Language Specification.
The answer to the question why it was so specified would be just speculation and not a real answer. My "educated guess", backed by Oli Charlesworth's, would be because the equivalent semantics apply to C and other similar languages. And the semantics in C are such (again an "educated guess") because they allow the compiler to produce the most optimal code.
I understand why the following is wrong:
byte a = 3;
byte b = 8;
byte c = a + b; // compile error
It won't compile. Expressions always result in an int. So we should have done the explicit cast:
byte c = (byte) (a + b); // valid code
But I don't understand why the following is correct:
byte d = 3 + 8; // it's valid! why?
Because literal integer (such as 3 or 8) is always implicitly an int. And int-or-smaller expressions always result in an int too. Can anybody explain what is going on here?
The only thing I can guess is that the compiler equates this expression to the following:
byte d = 11;
and doesn't consider this an expression.
This has less† to do with whether or not 3 + 8 is evaluated to 11 at compile-time, and more to do with the fact the compiler is explicitly permitted to implicitly narrow ints to bytes in certain cases. In particular, the language specification explicitly permits implicit narrowing conversions to byte of constant expressions of type int that can fit in a byte at compile-time.
The relevant section of the JLS here is section §5.2:
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 type of the
variable is byte, short, or char, and the value of the constant
expression is representable in the type of the variable.
The compile-time narrowing of constants means that code such as:
byte theAnswer = 42;
is allowed. Without the narrowing, the fact that the integer literal 42 has type int would
mean that a cast to byte would be required:
†: Obviously, as per the specification, the constant expression needs to be evaluated to see if it fits in the narrower type or not. But the salient point is that without this section of the specification, the compiler would not be permitted to make the implicit narrowing conversion.
Let's be clear here:
byte a = 3;
byte b = 8;
The reason that these are permitted is because of the above section of the specification. That is, the compiler is allowed to make the implicit narrowing conversion of the literal 3 to a byte. It's not because the compiler evaluates the constant expression 3 to its value 3 at compile-time.
The only thing I can guess is that the compiler equates this expression to the following:
Yes it does. As long as the right side expression is made of constants (which fit into the required primitive type -- see #Jason's answer for what the JLS says about this exactly), you can do that. This will not compile because 128 is out of range:
byte a = 128;
Note that if you transform your first code snippet like this:
final byte a = 3;
final byte b = 8;
byte c = a + b;
it compiles! As your two bytes are final and their expressions are constants, this time, the compiler can determine that the result will fit into a byte when it is first initialized.
This, however, will not compile:
final byte a = 127; // Byte.MAX_VALUE
final byte b = 1;
byte c = a + b // Nope...
The compiler will error out with a "possible loss of precision".
It is because 3 and 8 are compile time constants.
Therefore, at the time of compilation happens, compiler can identify that 3 + 8 can fit into a byte variable.
If you make your a and b to final (constant) variable. a + b will become a compile time constant. Therefore, it will compile without any issue.
final byte a = 3;
final byte b = 8;
byte c = a + b;
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Eclipse bug? When is a short not a short?
In Java I can assign a short or a byte inside blocks like this:
short s = 20000;
byte b = 120;
Because 20000 is a short value and 120 is a byte value, if I attempt to try:
short s = 67000;
byte b = 128;
I will get an error that states it cannot compile from int to byte where
byte b = (byte)12232;
will not even trigger a warning that value may get lost. In a function call I'm forced to cast it anyways:
void test(short s){}
test(1) //invalid
test((short)1) //valid
While on return types it is okay again.
short test(){
return 1; //valid
}
Why does the compiler sometimes deside to know the bounds of numbers (at least from constants), while it seem that it forgets it elsewhere?
Integral numeric literals in Java are of just two kinds: int (which are declared as 123), and long (which are declared as 123L).
short s = 1 is allowed because it is an assignment and a narrow conversion is allowed there
short s = 67000 doesn't work because you are exceeding the maximum value that can rapresented with a short
short s = (short)67000 works because you are placing a cast so you are explicitly requesting the type checker to ignore the fact that 67000 exceeds the maximum value
test(1) doesn't work because it's a method invocation, a narrow conversion is not allowed (int -> short) and 1 is always evaluated as an integer literal by the compiler
This may sound too trivial for an intermediate Java programmer. But during my process of reviewing Java fundamentals, found a question:
Why is narrowing conversion like:
byte b = 13;
will be allowed while
int i = 13;
byte b = i;
will be complained by the compiler?
Because byte b = 13 ; is assignment of a constant. Its value is known at compile time, so the compiler can/should/will whine if assignment of the constant's value would result in overflow (try byte b = 123456789 ; and see what happens.)
Once you assign it to a variable, you're assigning the value of an expression, which, while it may well be invariant, the compiler doesn't know that. That expression might result in overflow and so the compiler whines.
From here:
Assignment conversion occurs when the
value of an expression is assigned
(§15.26) to a variable: the type of
the expression must be converted to
the type of the variable. Assignment
contexts allow the use of an identity
conversion (§5.1.1), a widening
primitive conversion (§5.1.2), or a
widening reference conversion
(§5.1.4). In addition, a narrowing
primitive conversion may be used if
all of the following conditions are
satisfied:
The expression is a constant expression of type byte, short, char or int.
The type of the variable is byte, short, or char.
The value of the expression (which is known at compile time, because it is a constant expression) is representable in the type of the variable.
In your example all three conditions are satisfied, so the narrowing conversion is allowed.
P.S. I know the source I'm quoting is old, but this aspect of the language hasn't changed since.
Because a literal number has no type.
Once you give it a type it must be casted to the other one:
int i = 13;
byte b = (byte) i;
A byte has 8 bits. An int, 32 bits, and it is a signed number.
Conversion from a number with a smaller range of magnitude ( like int to long or long to float ) is called widening. The goal of widening conversions is to produce no change in the magnitude of the number while preserving as much of the precision as possible. For example, converting the int 2147483647 to float produces 2.14748365e9 or 2,147,483,650. The difference is usually small, but it may be significant.
Conversely, conversion where there is the possibility of losing information about the magnitude of the number ( like long to int or double to long ) is called narrowing. With narrowing conversions, some information may be lost, but the nearest representation is found whenever possible. For example, converting the float 3.0e19 to long yields -9223372036854775807, a very different number.