Integer arithmetic in Java with char and integer literal
'a' + 10
is a compile-time constant expression with the value of 'k'
, which can initialise a variable of type char
. This is the same as being able to assign a byte
variable with a literal integer in [-128, 127]. A byte
in the range of [128, 255] may be more annoying.
Java char addition (char1 = char2 + 10)
When you add numbers, they undergo binary numeric promotion. That is, the operands are widened to allow them to be added.
When you add a char
to an int
, the char
is widened to an int
, and the result is an int
.
As such, you would have to cast the int
back to a char
:
char a = (char) (c + 10);
However, even when adding a char
to another char
, both are widened to int
, and the result is again an int
, and so cannot be assigned to a char
variable. The rules are basically:
- If either operand is a double, widen both to double
- Else, if either operand is a float, widen both to float
- Else, if either operand is a long, widen both to long
- Else, widen both to int
So, even if you were adding a byte
to a byte
, both are widened to int
, added, and the result is an int
.
The exception to this is if you made c
final
:
final char c = 34;
In that case, c
has a compile-time constant value, so c + 10
is a compile-time constant expression. Because it's a compile-time constant expression, the compiler knows its value, and knows that it fits into the range of char
; so it would be allowed:
final char c = 34;
char a = c + 10;
Why int a = 5 - '0'; is possible in java?
Doc:
char: The char data type is a single 16-bit Unicode character. It has a minimum value of '\u0000' (or 0) and a maximum value of '\uffff' (or 65,535 inclusive).
In this case it is casted to int, that is why it works.
Correction
This is called Widening Primitive Conversion. Thanks @Andreas!
+1 curiosity
public static void main(String[] args) {
int a = 5 - '0';
}
I compiled this to bytecode:
public static main([Ljava/lang/String;)V
L0
LINENUMBER 24 L0
BIPUSH -43
ISTORE 1
L1
LINENUMBER 25 L1
RETURN
L2
LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
LOCALVARIABLE a I L1 L2 1
MAXSTACK = 1
MAXLOCALS = 2
}
Notice the line with BIPUSH -43
which means this value is calculated buildtime not runtime!
In Java, is the result of the addition of two chars an int or a char?
The result of adding Java chars, shorts, or bytes is an int:
Java Language Specification on Binary Numeric Promotion:
- If any of the operands is of a reference type, unboxing conversion
(§5.1.8) is performed. Then:- If either operand is of type double, the
other is converted to double.- Otherwise, if either operand is of type
float, the other is converted to float.- Otherwise, if either operand
is of type long, the other is converted to long.- Otherwise, both
operands are converted to type int.
But note what it says about compound assignment operators (like +=):
The result of the binary operation is converted to the type of the left-hand variable ... and the result of the conversion is stored into the variable.
For example:
char x = 1, y = 2;
x = x + y; // compile error: "possible loss of precision (found int, required char)"
x = (char)(x + y); // explicit cast back to char; OK
x += y; // compound operation-assignment; also OK
One way you can find out the type of the result, in general, is to cast it to an Object and ask it what class it is:
System.out.println(((Object)('a' + 'b')).getClass());
// outputs: class java.lang.Integer
If you're interested in performance, note that the Java bytecode doesn't even have dedicated instructions for arithmetic with the smaller data types. For example, for adding, there are instructions iadd
(for ints), ladd
(for longs), fadd
(for floats), dadd
(for doubles), and that's it. To simulate x += y
with the smaller types, the compiler will use iadd
and then zero the upper bytes of the int using an instruction like i2c
("int to char"). If the native CPU has dedicated instructions for 1-byte or 2-byte data, it's up to the Java virtual machine to optimize for that at run time.
If you want to concatenate characters as a String rather than interpreting them as a numeric type, there are lots of ways to do that. The easiest is adding an empty String to the expression, because adding a char and a String results in a String. All of these expressions result in the String "ab"
:
'a' + "" + 'b'
"" + 'a' + 'b'
(this works because"" + 'a'
is evaluated first; if the""
were at the end instead you would get"195"
)new String(new char[] { 'a', 'b' })
new StringBuilder().append('a').append('b').toString()
String.format("%c%c", 'a', 'b')
Why we can not pass integer literal as argument to method with byte as formal parameter
First, Assignment Conversions, JLS 5.2 covers what values can be assigned.
In addition, if the expression is a constant expression (§15.28) of type
byte
,short
,char
, orint
:
- A narrowing primitive conversion may be used if the variable is of type
byte
,short
, orchar
, and the value of the constant expression is representable in the type of the variable.
For byte a = 0;
the constant expression is the int
0
which is narrowed to a byte
.
Next, Invocation Contexts, JLS 5.3 covers what values can be passed to methods.
Neither strict nor loose invocation contexts include the implicit narrowing of integer constant expressions which is allowed in assignment contexts.
Therefore, your code has a compiler error for the constant expression narrowing conversion that is disallowed in an invocation context (method call).
setByte(0); // no implicit narrowing conversion, even if it's a constant expression
void setByte(byte b){}
Your code 128 - 1
is a constant expression that is narrowed to a byte
.
However, 2147483648 - 1
is disallowed because 2147483648
itself is not a valid int
literal, Section 3.10.1, "Integer Literals".
It is a compile-time error if the decimal literal 2147483648 appears anywhere other than as the operand of the unary minus operator; or if a decimal literal of type int is larger than 2147483648 (231).
If you really want to use an unnecessarily complicated expression to initialize an int
, you can use a long
literal to make the expression legal:
2147483648L - 1
but then you must explicitly cast the expression to an int
; there is no implicit narrowing from anything wider than int
to an int
:
(int) (2147483648L - 1)
Strangely, you don't have to place parentheses around the expression so that the cast applies to the whole expression, although I strongly recommended to use parentheses for clarity.
(int) 2147483648L - 1 // It's 2147483647!
The int
cast on the long
literal, which is out of int
range, will yield -2147483648, a valid int
value. Subtracting 1 here involves overflow in the negative direction, yielding the intended value of 2147483647.
Related Topics
Junit Confusion: Use 'Extends Testcase' or '@Test'
Java Keylistener VS Keybinding
Using Javafx in Jre 8, "Access Restriction" Error
Visual Studio Code, Java Extension, How to Add a Jar to Classpath
When to Use Atomicreference in Java
Is It Not Possible to Have a Code in the Background Who Will Be Called Every 24H
Differencebetween 'E', 'T', and '' for Java Generics
Differencebetween Connection and Read Timeout for Sockets
How to Add Shapes on Javafx Linechart
Random "Element Is No Longer Attached to the Dom" Staleelementreferenceexception
How to Check If a Url Exists or Returns 404 with Java
Lambda Expression and Method Overloading Doubts
How to Convert Date in to Yyyy-Mm-Dd Format
Get the Number of Weeks Between Two Dates
Cors Allowed-Origin Restrictions Aren't Causing the Server to Reject Requests