Today while helping someone I came across an interesting issue which I couldn't understand the reason.
While using += we don't need to explicit casting, but when we use i+i, we need to explicitly cast. Couldn't find exact reason. Any input will be appreciated.
public class Test{
byte c = 2;
byte d = 5;
public void test(String args[])
{
c += 2;
d = (byte) (d + 3);
}
}
Java is defined such that += and the other compound assignment operators automatically cast the result to the type of the variable being updated. As a result, the cast isn't necessary when using +=, though it is necessary when just using the normal operators. You can see this in the Java Language Specification at http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.26.2
Specifically, the expression
a op= b
Is equivalent to
(a = (type of a)((a) op (b));
Hope this helps!
From the Java Language Spec, Chapter 15:
[..] the result of the binary operation (Note: (c+2) in our example, which results in an int type value) is converted to the type of the left-hand variable (Note: to byte in our example), subjected to value set conversion (§5.1.13) to the appropriate standard value set (not an extended-exponent value set), and the result of the conversion is stored into the variable.
Related
I have been testing the char casting and I went through this:
public class Test {
public static void main(String a[]) {
final byte b1 = 1;
byte b2 = 1;
char c = 2;
c = b1; // 1- Working fine
c = b2; // 2 -Compilation error
}
}
Can anyone explain why it's working fine in 1 when I added a final to the byte?
When the variable is final, the compiler automatically inlines its value which is 1. This value is representable as a char, i.e.:
c = b1;
is equivalent to
c = 1;
In fact, according to this section on final variables, b1 is treated as a constant:
A variable of primitive type or type String, that is final and initialized with a compile-time constant expression (§15.28), is called a constant variable.
The conversion from byte to char is a widening and narrowing primitive conversion, as described in paragraph 5.1.4 of the Java Language Specification.
As the JLS describes, this is done via an intermediate step; the byte is converted to int via a widening primitive conversion and then the int is converted to char via a narrowing primitive conversion (see 5.1.3).
Paragraph 5.2 explains when a cast is necessary when you do an assignment:
... 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.
Your variable b1 is indeed a constant, but your variable b2 is not, so this rule applies for b1 but not for b2.
So: you can assign b1 to c because b1 is a constant and the value of the constant, 1, fits in a char, but you cannot assign b2 to c without a cast because b2 is not a constant.
Well , its because byte is a signed type while char is not, so u need to apply explicit type conversion for (2)
c = (char)b2;
also the final statement worked for 1 because prior to compilation , the compiler is able to confirm that there is no loss due to conversion since '1' is in the range of char , try putting '-1' with the same final statement in (1) you will again get a compilation error.
All this boils down to the type compatibility between signed and unsigned types..which needs to be done explicitly in java.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
Have a look at the following code :
Case 1 :
char a = 'x' ^ 'y';
It is working fine.
But when I use variable instead of constants as here :
Case 2:
char x = 'x';
char y = 'y';
char a = x ^ y; // Error!
In java language : case 1 is working and value of a comes out to be 1 but case 2 is not working.
In C and C++ : both cases are working, and the value of a comes out to be 1
In C# : both the cases are not working.
In javascript : both cases are working, which is not a HLL, and value of a comes out to be 0.
I understand that java is converting variables to integer to do binary operation, but why it is working in case 1 and not in case 2, Why same is not working in C# And why the values are different in the case of javascript.
Updated
When I made the variables final than it is working in java, but still not in C#
final char x = 'x';
final char y = 'y';
char a = x ^ y;
But still I cannot understand why constants are working but when using variable they are not. And why same is not happening with other high level programming language.
I think it is a basic operation and should be working in all programming languages with same behaviour.
Note To test all above cases in javascript I am replacing 'char' with 'var' in all cases and they are working.
Answering for Java only.
The expression 'x' ^ 'y' is a constant expression; x ^ y is not, unless both variables are declared final. Furthermore, the result is an int; ^ is an integral bitwise operator, meaning that both operands have to be promoted to an integral type before being evaluated. char promotes to int.
So you have this int expression, and you try to narrow it to a char. In the general case, this could lead to a loss of precision (ints are 4 bytes, chars are 2), so the compiler doesn't let you do that without you explicitly stating it's what you want to do (via a cast to char). However, you can implicitly narrow constant expressions, if their value would fit into the new type. From JLS 5.2:
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.
(Emphasis added)
Intuitively, this makes total sense: the error is there to tell you that you may lose precision, and so it wants you to confirm that you know that; in a sense, it's a loud warning. But if the compiler can know absolutely that this won't happen, as it can for a constant expression, then it makes things a bit easier for you and "hides" that warning.
I will consider C languages that is C, C++ and C#.
In C# there is no implicit conversion from an integral type to type char. According to section "11.1.5 Integral types" of the ECMA 334 Standard "C# Specification"
• There are no implicit conversions from other types to the char type.
In particular, even though the sbyte, byte, and ushort types have
ranges of values that are fully representable using the char type,
implicit conversions from sbyte, byte, or ushort to char do not exist.
So you need explicitly to cast the result of the operator to type char. For example
using System;
namespace ExclusiveOr
{
class Program
{
static void Main(string[] args)
{
char a = ( char )('x' ^ 'y' );
char c = 'x', d = 'y';
char b = ( char )( c ^ d );
Console.WriteLine("a = {0}, b = {1}", (int)a, (int)b);
}
}
}
The output is
a = 1, b = 1
According to the C Standard (section 6.7.9 Initialization)
4 All the expressions in an initializer for an object that has static
or thread storage duration shall be constant expressions or string
literals
So for example this code will be compiled
#include <stdio.h>
char a = 'x' ^ 'y';
int main(void)
{
printf( "a = %d\n", a );
return 0;
}
The output is
a = 1
However this code will not be compiled
#include <stdio.h>
char c = 'x';
char d = 'y';
char b = c ^ d;
int main(void)
{
printf( "b = %d\n", b );
return 0;
}
will not be compiled. The GCC compiler will issue error
prog.c:8:1: error: initializer element is not constant char b = c ^ d;
However if you will make varaible b local then the code will be compiled successfully
#include <stdio.h>
char c = 'x';
char d = 'y';
int main(void)
{
char b = c ^ d;
printf( "b = %d\n", b );
return 0;
}
The output is
b = 1
In C++ there is no such a restriction for objects with the static storage duration so all examples of programs similar to C programs showed above will be compiled.
Under normal circumstances, the operands of the following binary operators are "widened" before the operation is performed.
(From the JLS 5.6.1)
The multiplicative operators *, /, and % (§15.17)
The addition and subtraction operators for numeric types + and - (§15.18.2)
The numerical comparison operators <, <=, >, and >= (§15.20.1)
The numerical equality operators == and != (§15.21.1)
The integer bitwise operators &, ^, and | (§15.22.1)
In certain cases, the conditional operator ? : (§15.25)
In the case of char operands, widening will convert the operands to int.
For the arithmetic and bitwise operations, the type of the operation's result is the same as the "wider" of the two operands.
When x and y have type char, the expression x ^ y gives you an int value. That can't be assigned back to a char without a type cast, and hence you get a compilation error.
For the case where you are using char literals, the same widening process happens. But there is a "special exception" in the language which allows the value of a constant expression to be implicitly narrowed, provided that the constant expression's value will fit into the type. In this case, 'x' ^ 'y' will "fit" into a char, so the assignment is allowed.
The JLS reference for this exception for constant expressions is JLS 5.2.
Can anyone explain this?
public class Test {
public static void main(String[] args) {
char c = 'A';
int i = 0;
boolean b = true;
System.out.println(b ? c : i);
System.out.println(b ? c : (char)i);
System.out.println(b ? c : 0);
System.out.println(b ? c : (char)0);
}
}
Output:
65
A
A
A
It sure looks strange from where I'm standing. I would have expected only As to print out. And moreover: how come when I substitute 0 for i the output changes? The output seems to be the same for all values of i, not just 0.
When you want to select apples vs oranges, you must promote one of them (the lesser one):
public class Test {
public static void main(String[] args) {
char c = 'A';
int i = 0;
boolean b = true;
System.out.println(b ? c : i); // Promoted to int ---> 65
System.out.println(b ? c : (char)i); // Not promoted ------> A (char vs char)
System.out.println(b ? c : 0); // Not promoted vs null/0
System.out.println(b ? c : (char)0); // Not promoted vs char
}
}
If there was a variable type such as nibble then you would not get a different output when selecting.
System.out.println(b ? c : (nibble)i); // I know there is no nibble. :)
// nibble promotes to char.
// I know... there is no nibble.
//so, output is A
From the Java Language Specification, about type conversion and promotion (see the text in bold)
EVERY expression written in the Java programming language has a type that
can be deduced from the structure of the expression and the types of the literals,
variables, and methods mentioned in the expression. It is possible, however, to
write an expression in a context where the type of the expression is not appropriate.
In some cases, this leads to an error at compile time. In other cases, the context may
be able to accept a type that is related to the type of the expression; as a convenience,
rather than requiring the programmer to indicate a type conversion explicitly, the
Java programming language performs an implicit conversion from the type of the
expression to a type acceptable for its surrounding context.
The type conversion in your case that happens at compile time explains the output.
In the cases where i was involved, c has been promoted to integer.
In the cases where 0 was involved, it is treated as character and hence c remained as character.
I am using Java and new to it.
When I try
int integerValue = 100;
long longValue = 100;
integerValue = integerValue + longValue;
I get "Type mismatch: cannot convert from long to int".
But
integerValue+=longValue;
works fine, which means it is doing the cast for me :)
Is it something that "+=" provides inherently? Any specific reason for that?
Edit: Oops!! Too common question! :) I should have thoroughly searched first, my bad!!
Yes, this is behaving exactly as section 15.26.2 of the JLS explains.
A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T) ((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once.
You have uncovered the Tweedledum puzzle from the Java Puzzlers book.
Basically the compound assignment performs type cast and regular one doesn't. See JLS for details.
Using the operation assignment operators, there is an implicit cast.
int i = 10;
i *= 5.5;
// same as
i *= (int) (i * 5.5);
or even
char ch = '5';
ch /= 1.1; // ch = '0'
That's correct. In your first example, you have to cast to int, because the result result of the left expression is a long value (only because longValue is long).
integerValue = (long) (integerValue + longValue); // this works
The += operator does the (same) casting implicity.
Both behaviours are specified in the Java Language Specification.
Thanks everyone!
All answers were good enough!
Better explanations and examples available at stackoverflow.com/questions/8710619/java-operator :)
I have the following code snippet.
public static void main(String[] args) {
short a = 4;
short b = 5;
short c = 5 + 4;
short d = a;
short e = a + b; // does not compile (expression treated as int)
short z = 32767;
short z_ = 32768; // does not compile (out of range)
test(a);
test(7); // does not compile (not applicable for arg int)
}
public static void test(short x) { }
Is the following summary correct (with regard to only the example above using short)?
direct initializations without casting is only possible using literals or single variables (as long as the value is in the range of the declared type)
if the rhs of an assignment deals with expressions using variables, casting is necessary
But why exactly do I need to cast the argument of the second method call taking into account the previous summary?
These are the relevant JLS sections:
JLS 5.1.1 Identity Conversion
A conversion from a type to that same type is permitted for any type.
JLS 5.2 Assignment Conversion
Assignment conversion occurs when the value of an expression is assigned to a variable: the type of the expression must be converted to the type of the variable. Assignment contexts allow the use of one of the following:
Identity conversion
[...]
In addition, if the expression is a constant expression 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 above rules explain all of the following:
short a = 4; // representable constant
short b = 5; // representable constant
short c = 5 + 4; // representable constant
short d = a; // identity conversion
short e = a + b; // DOES NOT COMPILE! Result of addition is int
short z = 32767; // representable constant
short z_ = 32768; // DOES NOT COMPILE! Unrepresentable constant
As to why this doesn't compile:
test(7); // DOES NOT COMPILE! There's no test(int) method!
It's because the narrowing conversion with constant is only defined for assignments; not for method invocation, which has entirely different rules.
JLS 5.3. Method Invocation Conversion
Method invocation conversions specifically do not include the implicit narrowing of integer constants which is part of assignment conversion. The designers of the Java programming language felt that including these implicit narrowing conversions would add additional complexity to the overloaded method matching resolution process.
Instead of explaining how method resolution works precisely, I will just quote Effective Java 2nd Edition, Item 41: Use overloading judiciously:
The rules that determine which overloading is selected are extremely complex. They take up thirty-three pages in the language specification, and few programmers understand all of their subtleties.
See also
Varying behavior for possible loss of precision
short x = 3; x += 4.6; compiles because of semantics of compound assignment
The result of an arithmetic operation on short values is always int. test(7) doesn't work, since you haven't said that 7 is of type short. The compiler should be a bit smarter here.
The '7' in the call test(7); is an int and will not be automatically converted to a short.
It works when you declare and initialize short values, but that's a special case for the compiler. This special case doesn't exist for method calls.