Java - short and casting - java

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.

Related

Abnormal method overloading result in Java

Could you explain in a detailed manner why the expected result is not correct? As most of the readers expect that the output is Byte Char Int Byte, but of course it is not the correct answer.
public class Tester {
public static String test(byte b) {
return "Byte ";
}
public static String test(char c) {
return "Char ";
}
public static String test(int i) {
return "Int ";
}
public static void main(String[] args) {
byte b = 0;
char c = 'A';
System.out.print(test(true ? b : c));
System.out.print(test(false ? b : c));
System.out.print(test(true ? 0 : 'A'));
System.out.print(test(false ? 'A' : (byte)0));
}
}
The correct result is Int Int Char Int.
This is because overload resolution depends on the compile-time type of the argument expressions, not the runtime type of the argument. See JLS 15.12.2.2:
The argument expression true ? b : c has the type int, so the Int method is called. This is specified in the JLS 15.25.2, where it is said that if none of the special cases match (none of them do here), then the type of the conditional expression is the type after applying numeric promotion to the second and third operands:
An expression appears in a numeric choice context if the expression is one of the following:
The second or third operand of a numeric conditional expression
[...]
Numeric promotion determines the promoted type of all the expressions in a numeric context. [...] The rules are as follows:
[...] (none of these rules match in the case of a short and a byte expression)
Otherwise, none of the expressions are of type double, float, or long. In this case, the kind of context determines how the promoted type is chosen.
In a numeric choice context, the following rules apply:
[...] (none of these rules match in the case of a short and a byte expression)
Otherwise, the promoted type is int, and all the expressions that are not of type int undergo widening primitive conversion to int.
This is why true ? b : c is of type int. The same goes for false ? b : c and false ? 'A' : (byte)0. It doesn't matter what the condition is. The type of the expression is determined by the types of the second and third operands.
As for test(true ? 0 : 'A'), this can be explained by a rule that I omitted above, just before the last "Otherwise..."
Otherwise, if any expression is of type char, and every other expression is either of type char or a constant expression of type int with a value that is representable in the type char, then the promoted type is char, and the int expressions undergo narrowing primitive conversion to char.
In this case, the expression "0" is a "a constant expression of type int with a value that is representable in the type char".
One way to think about the type of a conditional expression, is that it's the smallest type that the expressions of both of the branches can "fit". For a byte b and a char c, int is the smallest type that can fit both of them. For 0, and 'A', char is enough to fit both, as it is known that 0 is in the range of char.
For the type of a conditional expression in Java, always consult the JLS charts (Section 15.25).
3rd →
byte
short
char
int
2nd ↓
 
 
 
 
byte
byte
short
bnp(byte,char)
byte | bnp(byte,int)
Byte
byte
short
bnp(Byte,char)
byte | bnp(Byte,int)
short
short
short
bnp(short,char)
short | bnp(short,int)
Short
short
short
bnp(Short,char)
short | bnp(Short,int)
char
bnp(char,byte)
bnp(char,short)
char
char | bnp(char,int)
where the notation "bnp(x, y)" means the type that is the result of binary numeric promotion of x and y, and the form "T | bnp(..)" means that if one operand is a constant expression of type int and may be representable in type T, then binary numeric promotion is used if the operand is not representable in type T.
For the first 2 calls, we have a char and a byte, so the result is "bnp(byte, char)", or the type int.
For the third call, we have a constant expression of type int and a char, and the value is representable as a char (it's 0), so the result is the type char.
For the fourth call, we have a byte and a char, so we back to "bnp(byte,char)", which is the type int.
The ternary operator converts its expressions to a common type in order for a variable to store its result.
Imagine if you had an expression like this:
char v = <myCondition> ? 'a' : 0L;
The previous statement would make sense only if the condition was true, but what about if this was false and 0L was to be returned? long and char are not the same data type. So, the compiler needs to find a common type, without losing precision, in order to allow both expressions to be returned and assigned to a variable suitable for both of them. The resulting data type is picked up regardless the condition's outcome.
In this case, long cannot be cast to int and then char as it would lose precision whereas char could be cast to int and then promoted to long. This is why the previous statement can be correctly written like so:
long v = <myCondition> ? 'a' : 0L;
In your example, the first ternary operator has a byte and a char, a byte can be promoted to an int while a char can be cast to an int. The other way around, where a char would be cast to a byte is not possible as this would cause a precision loss (a char occupies two bytes). By applying the same logic of promotion, you can also explain the remaining cases. Except for the third one, where you should apply the resolving table from the previously provided link to the documentation.
Table 15.25-A in the Java language specification describes what type the result of a ?: operator is, depending on the contributing types.
Your first example involves byte and char, so the result is a bnp (binary numeric promotion) of these, and for anything but double, float, or long, the resulting type is int.
Same thing for the others.

Promotion of primitive types

I have a question about the promotion of primitive types in Java. As we can see in the following example, one of the methods does not compile due to an error of type mismatch. Each method returns the same value but in different types.
The version of primitive long method works without error while the version of wrapper class Long fails. This is because the int literal in the return statement will be first promoted to a broader primitive type (e.g. long) and then to the corresponding wrapper class Integer and so on. Since Integer is not a subclass of Long the compiler gives an error.
But why does the version of wrapper class Byte works without any error? What exactly does the compiler do at this point?
long getPrimitiveLong() {
return 12; // valid
}
Long getWrapperLong() {
return 12; // Error: type mismatch
}
Byte getWrapperByte() {
return 12; // valid
}
The version with Byte works through some compiler magic.
Unlike long numeric literals which can be constructed with a L suffix, e.g. 12L, there is no such thing as a byte literal. That is why Java compiler treats numeric literals that fit in a byte as byte literals. Hence, 12 in your last example is considered a constant of type byte.
Java Language Specification offers a description of this conversion in section 5.2:
A narrowing primitive conversion followed by a boxing conversion may be used
if the type of the variable is:
Byte and the value of the constant expression is representable in the type byte.
Short and the value of the constant expression is representable in the type short.
Character and the value of the constant expression is representable in the type char.
This is because Java allows 1 conversion or Autoboxing, not more.
Java can do all these:
int i = 5;
double d = i; // int to double
long l = i; // int to long
long l = d; // double to long
Or autobox:
Integer i = 5; // int to Integer
Double d = 5.0; // double to Double
Long l = 5L; // long to Long
Converting twice, say int to Double, gives Java a hard time.
number like 12 consider as int by default by the compiler that is why the error
To fix that you can use casting for byte and place L after the value of long variable.
Read following post for more details
http://javaseeeedu.blogspot.com/2015/12/casting-part-1.html
As a short answer - try to replace 12 with 128 (byte is in range -128 to 127).
It won't compile, right?
The outcome here is that the compiler knows about byte boundaries.
For the in-depth answer you can do a deep dive into OpenJDK.

Java char to byte casting

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.

Why is the following Java program giving strange output?

I found below puzzle in Java Puzzlers,
public class DosEquis {
public static void main(String[] args) {
char x = 'X';
int i = 0;
System.out.print(true ? x : 0);
System.out.print(false ? i : x);
}
}
I tried this code and run it as well but output was not came as per my guess, my guess output should be : XX but in reality output is : X88
I tried lot to understand but i couldn't,can anybody give us the explanation? why is the different output is coming ? since i was able to understood that first print() that will print variable char x character value, but second print() printing 88 an ASCII- representation of value in char x.if i simplify the ternary operators expression in second print() like this
if(false){
System.out.print(i);
}else{
System.out.print(x);
}
then output is coming XX,quite strange, can anybody lighten this problem?
It would be great help for me to understand ternary operators.
Thanks in advance!
The reason for this behavior is that a ternary has one result type that the compiler has to choose in advance, and the flavor of print called in response.
In the true ? x : 0 case, 0 is treated as a char value, and print(char) is invoked. In the second case, since i is an int, x is also implicitly cast to int (a widening cast) and print(int) is invoked, yielding numeric output. The opposite, casting i to char implicitly, is illegal, because it could lose precision.
The implications of statically resolving the type can be shown with this example - not with print, because there is a print(Object), but consider this:
void method(boolean b);
void method(Integer i);
...
method(cond? false:0);
No matter what cond is, there is an overload compatible with the parameter. However, the compiler needs to chose one overload, which is not possible at compile time. The compiler will autobox both and assign the expression as Object*, but there is no method(Object).
*actually, my compiler said "The method method(boolean) in the type Test is not applicable for the arguments (Object&Serializable&Comparable<?>)", but the point stands.
Whatever result you are seeing is in accordance with the rules specified in JLS 15.25 for determining the type of conditional operation. Here is the point that it mentions:
If the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases:
If one of the operands is of type byte or Byte and the other is of type short or Short, then the type of the conditional expression is short.
If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression (§15.28) of type int whose value is representable in type T, then the type of the conditional expression is T.
If one of the operands is of type T, where T is Byte, Short, or Character, and the other operand is a constant expression (§15.28) of type int whose value is representable in the type U which is the result of applying unboxing conversion to T, then the type of the
conditional expression is U.
Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.
In your question, the last point is followed. x is char type while i is of int type , so x is promoted to int . Hence the output for the second line is 88 which is the int conversion of char X.
To understand what is happening here, we need to track the types. Lets consider each print statement in turn, first:
System.out.print(true ? x : 0);
Here the types are boolean ? char : const int
The compiler will want the ternary statement to return a single type, it cannot return either a char or an int. It spots that 0 can be a converted to char constant, and so treats it as the expression as 'boolean ? char : char'. Where the last char has the value zero. Because the boolean was true, the first char is printed as an X.
System.out.print(false ? i : x);
Here the types are boolean ? int : char, the int is an int variable and can not be treated as a char so the choice for the compiler is to either error as the types are different, narrow the int or widen the char. Narrowing the int would loose precision which, and so the compiler would not do it behind our back (it would require an explicit cast). Widening does not loose precision so the char is converted to an int. The int value of 'X' is 88. Thus the value 88 is printed.
More details of this conversion process can be read here: Java Language Spec
In this line :
System.out.print(false ? i : x);
X is casted to int, that's way prints the decimal value of char X
When you use a ternary operator condition?valTrue:valFalse . This will return the same datatype in both the case (true or false) . in line System.out.print(condition? i : x); you are aspecting int if true and Char if false , but it will return only int in both the case . similarly in line System.out.print(condition? x : i); in bothe case it will return Char .
In the first expression one side is a char, and 0 is assignable to a char; hence the first ternary expression is a char. char zero = 0;
In the second there are int and char, and the char is widened: (int)'X' is 88.
Constants like in c ? "" : null hence do not make an Object but a String (here).
Your observation is correct. But the ternary operator deal with the same data type. and it consider the data type of true vale and convert the type of false value if required. In your case at first print statement the ternary operator will consider data type as Char as the data type of x and at second print statement the ternary operator will consider data type as int as the data type of i that's the reason you are getting ASCII- representation of value in char x.
System.out.print(true ? x : 0);
/*ternary operator(compiler) consider the resulting data
type as char as the true Value type is char.*/
System.out.print(false ? i : x);
/*ternary operator(compiler) consider the resulting data
type as int as the true Value type is int.*/
/* but if you replace the print statement with if else condition
then there is no point of ternary concept at all*/
In a ternary statement typed like this: (boolean ?if type:else type), only the if type and the else type matters for the determination of the final type. The compiler deals with the situation as follows:
If both the if type and else type are same, then the final type is also the same.
If the if type can be converted to else type without loss of precision, the final type is the same as the else type.
If the else type can be converted to if type without loss of precision, the final type is the same as the if type.
If both 2 and 3 are possible, the final type is the same as the type of the lower data range.
If none of the above qualify, the compiler throws a compilation error.*
Let us take these various versions:
System.out.println(true ? x : 0); //X
This becomes X because the if type is char, and the else type is any type that can represent 0. The constant 0 is valid in the char data range (0 to 65,535). It is possible to treat x and 0 as int, but the int data range is higher (-231 to 231-1).
As per point 4, compiler picks the type of lower range (char).
System.out.println(true ? x : i); //88
Here, the if type is a char, and the else type is an int. When you declare the variable as a non-final int, the compiler cannot be sure that the value will never change from 0 (even if your code doesn't change it anywhere!).
Hence only point 2 applies here. The if type char can be converted to else type int without any loss in precision (as int has higher range), but if the else type int is converted to the if type char, the lower range of char can cause loss of precision (the value of i could be outside the range of char).
Therefore the compiler picks int to avoid loss of precision, as per point 2.
Some other test cases (similar reasons as already explained above):
System.out.println(false ? i : x); //88 (as per point 3)
System.out.println(false ? 0 : x); //X (as per point 4)
However, in this code, the result is different:
final int i = 0;
System.out.println(true ? x : i); //X
System.out.println(false ? i : x); //X
Can you tell why?
* Note: All types can ultimately convert to Object with no loss, including primitive types which can be auto-boxed (on recent Java versions). If the method is overloaded to accept Object type, you may never experience this case as the compiler converts the final type to Object.

Interesting observation on byte addition and assignment

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.

Categories