The following code gives error (with IDEA) while I think it shouldn't.
Long[] a = {0L, 0L};
Long[] b = {1L, 1L};
if((a[0] + a[1]).equals(b[1]))
System.out.println("Equal");
Says cannot resolve method equals(java.lang.Long). But it works well with if(a[0].equals(b[0])). I thought that plus operator would return a Long object.
Why does it seem like it doesn't return a Long object, and how are we able to use Long c = a[0] + a[1] if it doesn't return a Long object? Or why can't we use equals like that?
Why does it seem like it doesn't return a Long object?
15.18.2. Additive Operators (+ and -) for Numeric Types tells us that:
Binary numeric promotion is performed on the operands.
The type of an additive expression on numeric operands is the promoted type of its operands.
And 5.6.2. Binary Numeric Promotion tells us that:
If any operand is of a reference type, it is subjected to unboxing conversion.
What this means is that the result of Long + Long is long and we can't call methods on a primitive type.
And how are we able to use Long c = a[0] + a[1] if it doesn't return a Long object?
For Long c = a[0] + a[1], the long is boxed by the assignment.
a[0] + a[1] are added as primitive types and are not autoboxed (primitives do not have methods, hence compile time error). You need to either explicitly wrap them into an object:
(new Long(a[0] + a[1])).equals(b[1])
...or rely on the unboxing of b[1] into a primitive type
a[0] + a[1] == b[1]
(a[0] + a[1])
results in primitive long and not in reference type java.lang.Long.
If you're trying to use a member of a primitive type, that results in a compile time error.
You could use autoboxing, to convert the result of the addition back to Long like this:
((Long)(a[0] + a[1])).equals(b[1])
Long c = (a[0] + a[1]); does something like that "internally", i.e. actually works like this:
Long c = (Long)((long)a[0] + (long)a[1]);
You could also simply unbox b[1]:
(a[0] + a[1]) == b[1]
All you need to do is replace this line:
if((a[0] + a[1]).equals(b[1]))
for this:
if(a[0] + a[1] == b[1])
EDIT:
Yes, you're right - equals can't take sum of longs as parameter, from other answers I can see that it's because they are primitive values. Good to know that, we learn everyday :)
Related
I read that compiler refuses to use auto boxing/unboxing when there is more than one operation needed in order to perform implicit conversion (int -> double -> Double or Integer -> int -> double). It's stil rather confusing
Integer i = 2;
Double d = 2.0;
System.out.println(i == d); // COMPILE TIME ERROR
// fix
System.out.println( (double) i == d); // OK: true
My understanding is that the compiler tries to unwrap i with Integer.intValue(). Since it takes more than one step to convert Integer to double (Integer -> int -> double) compiler refuses to do it implicitly, so we need to "help him" by using explicit type conversion, which will reduce the number of steps. Is it correct?
Integer i = 2;
double d = 2.0;
System.out.println(i == d); // OK: true
In this example it clearly takes more than one step for the compiler to perform conversion (Integer -> int -> double). How come it doesn't complain?
I'm aware that one must use equals() method over ==
The answer can be found in the Java Language Specification, in section 15.21. Equality Operators:
The operators == (equal to) and != (not equal to) are called the equality operators.
Section 15.21.1. Numerical Equality Operators == and != says:
If the operands of an equality operator are both of numeric type, or one is of numeric type and the other is convertible (§5.1.8) to numeric type, binary numeric promotion is performed on the operands (§5.6).
Section 15.21.3. Reference Equality Operators == and != says:
If the operands of an equality operator are both of either reference type or the null type, then the operation is object equality.
It is a compile-time error if it is impossible to convert the type of either operand to the type of the other by a casting conversion (§5.5). The run-time values of the two operands would necessarily be unequal (ignoring the case where both values are null).
Without a cast, the i == d expression must follow the rules in section 15.21.3, because both i and d are reference types, not numeric types. Only the primitive types are numeric types (except boolean of course).
Since an Integer cannot be cast to a Double, and a Double cannot be cast to an Integer, the compiler knows that it is impossible for the expression to be true (ignoring the case where both values are null), so a compile-type error occurs.
When you do (double) i == d, then the left-hand side becomes a numeric type, and the rules specified in section 15.21.1 applies.
package practicejava;
public class Query {
public static void main(String[] args) {
char ch = 66;
System.out.println("character= " + ch);
ch++;
System.out.println("character = " + ch);
}
}
Technically ch++; and ch=ch+1; are the same but why do I get an error when I write ch=ch+1; instead of ch++;?
You need to provide a cast in order to do that :
ch = (char) (ch + 1);
This is because the expression ch + 1 is is promoted (upcast) to an int. In order for you to reassign this expression to a char you need to explicitly downcast it.
By ch+1, the char ch will be promoted to int first, just like ((int)ch) + 1, so the result will be an int.
When you try assign an int(32 bit) back to a char(16 bit), it might loss accuracy, you need to do it explictly ch = (char)(ch + 1);
This is called Binary Numeric Promotion:
Binary numeric promotion is performed on the operands of certain
operators:
...
The addition and subtraction operators for numeric types + and - (§15.18.2)
and it will perform
Widening primitive conversion (§5.1.2) is applied to convert either or
both operands as specified by the following rules:
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.
First of all, note that a char is 2 bytes large (16 bit), and an int is 32bit.
1. When typing ch++:
to apply the ++ operator, there is no type cast but the operator simply causes the bit represent of that char to increase by 1 to itself. Refer to JLS11 chapter 15.14.2,page 575:
The type of the postfix increment expression is the type of the variable.
2. When typing ch=ch+1:
ch is firstly casted to int, then it is added by 1(still an int), and the = is actually tring to cast the int which has 32bits into a char which has only 16 bits, note that this may lose accuracy. So without an explicitly cast, the compiler will complain about that, which is the cause of the error.
Looking at the Java Doc for Long, it only has two constructors:
1) primitive long as param --> new Long(10L);
2) String as param --> new Long("23");
But this works
new Long(23);
But if the literal is more than the int MAX VALUE (2147483647), the L suffix becomes mandatory so this: (
new Long(2147483648) will now require an L after the value
However:
new Long(Integer.MAX_VALUE + 1) is OK
Who can explain this?
The first part of your question is straightforward: passing int where long is required is allowed because any number that can be represented as an int can also be represented as a long. This is a so-called widening conversion.
The second part, about new Long(Integer.MAX_VALUE + 1), is trickier. Although the widening conversion occurs here as well, it is not of the value that one might think: if you run this program
Long x = new Long(Integer.MAX_VALUE + 1);
System.out.println(x);
you get -2147483648, not the expected 2147483648, because of int overflowing on addition (demo).
The compiler is not smart enough to promote parts of the expression to long before performing addition. Widening conversion occurs after the addition has been performed, on the result of the addition with an overflow.
You can find the answer in javadocs:
A widening primitive conversion from an integral type to another integral type, or from float to double in a strictfp expression (§15.4), does not lose any information at all; the numeric value is preserved exactly.
The integer value 23 can be converted to long 23L without loss of any information at all. Thus with the use of through widening primitive conversions, JVM implicitly converts it to 23L.
So when you call
new Long(23)
It becomes
new Long(23L)
When we write new Long(23) , you can think of it as something like the following happening :
int i = 23;
long l = i; // widening primitive conversion
Long newLong = new Long(l);
Widening conversions are allowed as per the JLS 5.1.2 , so there are no issues with it.
When we try instead , new Long(2147483648) , the first step itself fails, as the value is more than what can fit in an int
int i = 2147483648; // doesn't fit in int
When we try new Long(Integer.MAX_VALUE + 1), you can think of it as something like the following happening:
int i = Integer.MAX_VALUE + 1; // This gives the INTEGER.MIN_VALUE -2147483648
long l = i ; // widening primitive conversion
Long newLong = new Long(l);
So, this is allowed. Hope this helps.
The differences come from using literal or not.
By using a literal,
long1 = new Long(2147483648)
The code will not compile as the compiler checks the validity of the literal again the type of the variable that receives the literal value and 2147483648 is out of range for an int. So the compiler emits an error.
It is valid for any literal.
As the compiler expects to have a specific type as value of the literals, so it does the check :
The type of a literal is determined as follows:
The type of an integer literal (§3.10.1) that ends with L or l is long
(§4.2.1).
The type of any other integer literal is int (§4.2.1).
...
By using a computation :
long1 = new Long(Integer.MAX_VALUE + 1);
the compiler doesn't check if the size of the Long argument constructor is in the int range.
It is not a literal declaration.
At runtime, an int to long widening primitive conversion occurs.
It produces so a int with Integer.MAX_VALUE + 1 that is -2147483648 (overflow of int) that is converted to a long value.
This question already has answers here:
Java conditional operator ?: result type
(5 answers)
Why does the ternary operator unexpectedly cast integers?
(3 answers)
Closed 6 years ago.
Please consider we have code below:
Object obj = true ? new Integer(0) : new Long(1);
System.out.println(obj.getClass() + "\nvalue = " + obj);
And its result is:
class java.lang.Long
value = 0
Instead of:
class java.lang.Integer
value = 0
Could someone clarify why we have such functionality in Java? It is very strange for me.
Do you have any example where this can be useful?
UPDATE:
Here is piece of bytecode, where we can see, whats going on there
NEW java/lang/Integer
DUP
LDC "0"
INVOKESPECIAL java/lang/Integer.<init> (Ljava/lang/String;)V
INVOKEVIRTUAL java/lang/Integer.intValue ()I
I2L
INVOKESTATIC java/lang/Long.valueOf (J)Ljava/lang/Long;
ASTORE 1
What's going on here is the result of
Binary numeric promotion turning your Integer and Long types into long for use as the common type to apply to the conditional operator expression
Unboxing those wrapper objects
And then boxing the resulting value of the conditional expression
The conditional operator's second and third operands must end up having the same type, which is the resulting type of the expression. Integer and Long are not, of course, the same type.
However, as described in JLS§15.25, the compiler will apply binary numeric promotion when determining the possible common type to apply to the expression. That section has the handy Table 15.25-D which tells us that when the second operand is of type Integer and the third operand is of type Long, the compiler will do binary numeric promotion on Integer,Long. Binary numeric promotion on Integer,Long yields long. So the result of the conditional operator expression is long.
Since the expression's result type is long, the Integer or Long will have to be unboxed (and then cast, in the case of Integer).
Finally, you assign it to an Object, which forces boxing, and wraps the long in a Long. Thus, you end up with a Long containing the value 0, which matches your output.
So effectively, if we ignore the fact the compiler will optimize half of the following away since it's dealing with a constant expression thanks to the true in the code (I've used flag below instead), that code ends up being this:
Object obj = Long.valueOf(flag ? (long)(new Integer(0)).intValue() : (new Long(1)).longValue());
System.out.println(obj.getClass() + "\nvalue = " + obj);
The (long)(new Integer(0)).intValue() represents unboxing the Integer and casting it to long so it matches the expression result type.
The (new Long(1)).longValue() represents unboxing the Long so it matches the expresion result type.
And the Long.valueOf represents the boxing at the end.
This behavior is well explained in the JLS - 15.25. Conditional Operator ? ::
The conditional operator has three operand expressions. ? appears between the first and second expressions, and : appears between the second and third expressions.
[...]
The type of a conditional expression is determined as follows:
[...]
Otherwise, if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases:
[...]
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.
Actually long can store the value of an integer but integer can not store the value of long(Concept is widening) and you are storing it in Object that's why it's storing it in Long. internally it's also using boxing and unboxing
Possible compiler code:
Long obj = true ? new Integer(0) : new Long(1);
System.out.println(obj.getClass() + "\nvalue = " + obj);
Not working code(unable to compile):
Integer obj = true ? new Integer(0) : new Long(1);
System.out.println(obj.getClass() + "\nvalue = " + obj);
check by yourself and let me know if you havr any doubt in it.
This is related to how the ternary operator works.
The operands on both sides of the : must be of compatible types, so you can't have this:
true ? 1 : "Hello"
Since int and String cannot be implictly converted to each other.
In your case however, the types are compatible! An int can be implicitly converted to long. And that's what the compiler did! It saw an int and a long and decides that the expression should evaluate to a long. It unboxes the two values and implictly convert the int to a long. And finally, it boxes the resulting long so it becomes Long and put it in the variable.
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.