Widening and boxing with Java - java

In Java programming language widen and boxing doesn't work, but how does it work in following example?
final short myshort = 10;
Integer iRef5 = myshort;
Why does this work? Is this not the same as widen and then box?
But if I write the following code:
final int myint = 10;
Long myLONG = myint;
why it doesn't work?

Following what others have said, I can confirm that I can compile your first example with the Eclipse compiler, but not the second. With the javac compiler, both don't compile, as stated by Vlad
This seems to be a bug in either compiler! Let's consult the JLS to find out, which one is right :-)

With java 7 both the examples are not working. you will get below exception:
Type mismatch: cannot convert from short to Integer
Type mismatch: cannot convert from int to Long
Because the problem is not because of boxing but because of conversion.

You can either widen or box, but you can't do both.
You can do
final int myint = 10;
Long myLONG = (long) myint;

Neither works as it is (using javac 1.6.0_26 from Sun/Oracle, on Linux).
See also here.
b.java:4: incompatible types
found : short
required: java.lang.Integer
Integer iRef5 = myshort;
^
b.java:7: incompatible types
found : int
required: java.lang.Long
Long myLONG = myint;
^
2 errors

Let's examine what you're trying to do in detail:
final short myshort = 10;
Integer iRef5 = myshort;
The compiler will try to first box that short into an object, so that it can then perform the assignment (it cannot widen directly, since it is dealing with different types: an object and a primitive).
In short, this is equivalent to:
final short myshort = 10;
final Short box = new Short(myshort); // boxing: so that objects are assignable.
Integer iRef5 = box; // widening: this fails as Integer is not a superclass of Short
The same reasoning can be applied to your second example (which also fails), as is visible here. If your compiler does not complain on the first one, then there might be a bug with the compiler, because this is what's defined in the JLS. See the complete set of rules for conversion/promotion in the JLS here.

These are the promotion rules that apply to expressions in Java
all byte and short values are promoted to int
If one operand is long,the whole expression is promoted to long
If one operand is a float ,the whole expression is promoted to float
If one operand is double ,the whole expression is promoted to double
Hence short value is promoted to int. This is not widening.
"The conversion of a subtype to one of its supertypes is called widening"

You can box then wide but you can't widen and then box
So
final short myshort = 10;
Integer iRef5 = myshort;
is equivalent to
final short myshort = 10;
Integer iRef5 = 10;
which is perfectly valid
but
Long myLONG = 10;//this won't compile ,
But try with 10L it will so it will box then you can have it object too i.e. Object o = 10L;
See also
boxing-and-widening
scjp-cant-widen-and-then-box-but-you-can-box-and-then-widen

Related

What does "possible lossy conversion" mean and how do I fix it?

New Java programmers are often confused by compilation error messages like:
"incompatible types: possible lossy conversion from double to int"
for this line of code:
int squareRoot = Math.sqrt(i);
In general, what does the "possible lossy conversion" error message mean, and how do you fix it?
First of all, this is a compilation error. If you ever see it in an exception message at runtime, it is because you have have run a program with compilation errors1.
The general form of the message is this:
"incompatible types: possible lossy conversion from <type1> to <type2>"
where <type1> and <type2> are both primitive numeric types; i.e. one of byte, char, short, int, long, float or double.
This error happens when your code attempts to do an implicit conversion from <type1> to <type2> but the conversion could be lossy.
In the example in the question:
int squareRoot = Math.sqrt(i);
the sqrt method produces a double, but a conversion from double to int is potentially lossy.
What does "potentially lossy" mean?
Well lets look at a couple of examples.
A conversion of a long to an int is a potentially lossy conversion because there are long values that do not have a corresponding int value. For example, any long value that is greater than 2^31 - 1 is too large to be represented as an int. Similarly, any number less than -2^31 is too small.
A conversion of an int to a long is NOT lossy conversion because every int value has a corresponding long value.
A conversion of a float to an long is a potentially lossy conversion because there float values that are outside of the range that can be represented as long values. Such numbers are (lossily) convert into Long.MAX_VALUE or Long.MIN_VALUE, as are NaN and Inf values.
A conversion of an long to a float is NOT lossy conversion because every long value has a corresponding float value. (The converted value may be less precise, but "lossiness" doesn't mean that ... in this context.)
These are all the conversions that are potentially lossy:
short to byte or char
char to byte or short
int to byte, short or char
long to byte, short, char or int
float to byte, short, char, int or long
double to byte, short, char, int, long or float.
How do you fix the error?
The way to make the compilation error go away is to add a typecast. For example;
int i = 47;
int squareRoot = Math.sqrt(i); // compilation error!
becomes
int i = 47;
int squareRoot = (int) Math.sqrt(i); // no compilation error
But is that really a fix? Consider that the square root of 47 is 6.8556546004 ... but squareRoot will get the value 6. (The conversion will truncate, not round.)
And what about this?
byte b = (int) 512;
That results in b getting the value 0. Converting from a larger int type to a smaller int type is done by masking out the high order bits, and the low-order 8 bits of 512 are all zero.
In short, you should not simply add a typecast, because it might not do the correct thing for your application.
Instead, you need to understand why your code needs to do a conversion:
Is this happening because you have made some other mistake in your code?
Should the <type1> be a different type, so that a lossy conversion isn't needed here?
If a conversion is necessary, is the silent lossy conversion that the typecast will do the correct behavior?
Or should your code be doing some range checks and dealing with incorrect / unexpected values by throwing an exception?
"Possible lossy conversion" when subscripting.
First example:
for (double d = 0; d < 10.0; d += 1.0) {
System.out.println(array[d]); // <<-- possible lossy conversion
}
The problem here is that array index value must be int. So d has to be converted from double to int. In general, using a floating point value as an index doesn't make sense. Either someone is under the impression that Java arrays work like (say) Python dictionaries, or they have overlooked the fact that floating-point arithmetic is often inexact.
The solution is to rewrite the code to avoid using a floating point value as an array index. (Adding a type cast is probably an incorrect solution.)
Second example:
for (long l = 0; l < 10; l++) {
System.out.println(array[l]); // <<-- possible lossy conversion
}
This is a variation of the previous problem, and the solution is the same. The difference is that the root cause is that Java arrays are limited to 32 bit indexes. If you want an "array like" data structure which has more than 231 - 1 elements, you need to define or find a class to do it.
"Possible lossy conversion" in method or constructor calls
Consider this:
public class User {
String name;
short age;
int height;
public User(String name, short age, int height) {
this.name = name;
this.age = age;
this.height = height;
}
public static void main(String[] args) {
User user1 = new User("Dan", 20, 190);
}
}
Compiling the above with Java 11 gives the following:
$ javac -Xdiags:verbose User.java
User.java:20: error: constructor User in class User cannot be applied to given types;
User user1 = new User("Dan", 20, 190);
^
required: String,short,int
found: String,int,int
reason: argument mismatch; possible lossy conversion from int to short
1 error
The problem is that the literal 20 is an int, and the corresponding parameter in the constructor is declared as a short. Converting an int to a short is lossy.
"Possible lossy conversion" in a return statement.
Example:
public int compute() {
long result = 42L;
return result; // <<-- possible lossy conversion
}
A return (with a value / expression) could be thought of an an "assignment to the return value". But no matter how you think about it, it is necessary to convert the value supplied to the actual return type of the method. Possible solutions are adding a typecast (which says "I acknowledge the lossy-ness") or changing the method's return type.
"Possible lossy conversion" due to promotion in expressions
Consider this:
byte b1 = 0x01;
byte mask = 0x0f;
byte result = b1 & mask; // <<-- possible lossy conversion
This will tell you that you that there is a "possible lossy conversion from int to byte". This is actually a variation of the first example. The potentially confusing thing is understanding where the int comes from.
The answer to that is it comes from the & operator. In fact all of the arithmetic and bitwise operators for integer types will produce an int or long, depending on the operands. So in the above example, b1 & mask is actually producing an int, but we are trying to assign that to a byte.
To fix this example we must type-cast the expression result back to a byte before assigning it.
byte result = (byte) (b1 & mask);
"Possible lossy conversion" when assigning literals
Consider this:
int a = 21;
byte b1 = a; // <<-- possible lossy conversion
byte b2 = 21; // OK
What is going on? Why is one version allowed but the other one isn't? (After all they "do" the same thing!)
First of all, the JLS states that 21 is an numeric literal whose type is int. (There are no byte or short literals.) So in both cases we are assigning an int to a byte.
In the first case, the reason for the error is that not all int values will fit into a byte.
In the second case, the compiler knows that 21 is a value that will always fit into a byte.
The technical explanation is that in an assignment context, it is permissible to perform a primitive narrowing conversion to a byte, char or short if the following are all true:
The value is the result of a compile time constant expression (which includes literals).
The type of the expression is byte, short, char or int.
The constant value being assigned is representable (without loss) in the domain of the "target" type.
Note that this only applies with assignment statements, or more technically in assignment contexts. Thus:
Byte b4 = new Byte(21); // incorrect
gives a compilation error.
1 - For instance, the Eclipse IDE has an option which allows you to ignore compilation errors and run the code anyway. If you select this, the IDE's compiler will create a .class file where the method with the error will throw an unchecked exception if it is called. The exception message will mention the compilation error message.

Why does casting a primitive type to a reference type is giving compilation error? [duplicate]

This question already has answers here:
Java: Why can't I cast int to Long
(3 answers)
Closed 4 years ago.
I am wondering why casting a primitive data type (int for instance) to a reference type (Long for instance) does not compile?
BinaryOperator<Long> add = (x, y) -> x + y;
System.out.println(add.apply((Long)8, (Long)5)); //this line does not compile
System.out.println(add.apply((long)8, (long)5)); // this line does compile
I will be happy to have some detailed answer. Thank you.
Because this
Long l = 1;
means assigning an int (literal number without floating part are int) to an Object, here a Long.
The autoboxing feature introduced in Java 5 doesn't allow to box from an int to something else than a Integer. So Long is not acceptable as target type but this one would be :
Integer i = 1;
In your working example you convert the int to a long : (long)8.
So the compiler can perfectly box long to Long.
The long is a primitive data type, but Long is a (wrapper) class.
The following should work.
System.out.println(add.apply(Long.valueOf(8), Long.valueOf(5)));

casting to int while using Math.round(double d) in Java 1.8.0_152 [duplicate]

This question already has an answer here:
Casting rules for primitive types in java
(1 answer)
Closed 5 years ago.
I don't understand why code snippet 1 works fine, but code snippet 2 gives compile time error incompatible types: java.lang.Long cannot be converted to int
Is related to priority of casting in java? I went through this thread on stackoverflow, but it didn't help.
Please explain in detail what happens behind the scenes in the both the snippets and why it doesn't give error in code snippet 1.
Code snippet 1:
package com.company;
public class Main
{
public static void main(String[] args)
{
double dbl = 10.25;
int i1 = (int)Math.round(dbl);
}
}
Code snippet 2:
package com.company;
public class Main
{
public static void main(String[] args)
{
double dbl = 10.25;
Long lng = Math.round(dbl);
int i2 = (int)lng;
}
}
Let's take a closer look at what happens. In snippet 1:
double dbl = 10.25;
int i1 = (int) Math.round(dbl);
You declare a double. The result after rounding is a long. Then comes a cast to int which is possible since both are primitive types, Java allows the cast. Then you store this inside int.
In snippet 2 however things change a bit:
double dbl = 10.25;
Long lng = Math.round(dbl);
int i2 = (int) lng;
You declare a double, round returns a long but you want to store it inside Long. So Java automatically transforms long into Long, this is called auto-boxing. Java can box all primitive types to their wrapper classes.
Last you try to cast Long to int but this is not possible since Long is an object now. It can however automatically unbox Long to long. From there you can go to int. Let's first take a look at those two variants:
int i2 = (long) lng; // Make unboxing explicit by casting
int i2 = lng.longValue() // Use unboxing method
However it won't compile either due to:
incompatible types: possible lossy conversion from long to int
So the conversion is possible but the compiler warns us that a long could possibly not fit into an int (lossy conversion), thus it prevents us from doing so.
We can however do the conversion by again making it explicit by casting:
int i2 = (int) lng.longValue() // The whole process completely explicit
Or if you have Java 8, use Math#toIntExact (documentation) which will throw an ArithmeticException if the long doesn't fit into the int:
int i2 = Math.toIntExact(lng.longValue());
Note that Java won't unbox your Long to long and then cast it to int automatically, you will need to make the unboxing explicit on your own, as shown.
It's simply one of the casting rules of the JVM, in this case probably to prevent unintentional programming errors.

will we loose precision by implicit conversion

There is a question in Java past exam paper that brothers me:
With implicit conversion of primitive data types, you can lose precision and get incorrect results.
A True, B False
The key to the answer is A: True
I think it will neither lose precision nor get incorrect results. I know the explicit conversion can lose precision and get incorrect results but not implicit one.
For example:
int i = 9;
short s = 3;
i = s; // implicit conversion, neither loose
//precision nor incorrect results
s = i; // compile error, do we call this implicit conversion?
//if yes, then the answer to question 3 is True,
//but I don't think this is an implicit conversion,
//so I think answer is false.
As states on the notes:
Implicit type conversion: The programmer does not make any attempt to convert the type, rather the type is automatically converted by the system under certain circumstances.
Could anyone please advise?
Many thanks.
Answer = A
float f = Long.MAX_VALUE;
System.out.println(Long.MAX_VALUE);
System.out.printf("%.0f", f);
output
9223372036854775807
9223372036854776000
There are some cases where the compiler will allow an implicit conversion, but you may still lose precision. For example:
long a = Long.MAX_VALUE; // 9223372036854775807
double b = a; // 9223372036854776000
See the JLS for more details on this.
There is implicit conversions in assignment operators. These can lose precision or cause an overflow. For regular assignments, implicit conversion only happen when the compiler knows it is safe. It can still lose precision, but not cause an overflow.
e.g.
final int six = 6;
byte b = six; // compiler uses constant propagation and value is in range.
int five = 5;
byte b2 = five; // fails to compile
double d = 5.5;
five += d; // compiles fine, even though implicit conversion drops the 0.5
// five == 10 not 10.5
five += Double.NaN; // five is now 0 ;)

Java: Long result = -1: cannot convert from int to long

I'm using eclipse java ee to perform java programming.
I had the following line of code in one of my functions:
Long result = -1;
I got the following error:
Type mismatch: cannot convert from int to Long
I can't quite understand why when i add a number to the variable it provides this error.
How can this issue be resolved and why did it happen in the first place?
There is no conversion between the object Long and int so you need to make it from long. Adding a L makes the integer -1 into a long (-1L):
Long result = -1L;
However there is a conversion from int a long so this works:
long result = -1;
Therefore you can write like this aswell:
Long result = (long) -1;
Converting from a primitive (int, long etc) to a Wrapper object (Integer, Long etc) is called autoboxing, read more here.
-1 can be auto-boxed to an Integer.
Thus:
Integer result = -1;
works and is equivalent to:
Integer result = Integer.valueOf(-1);
However, an Integer can't be assigned to a Long, so the overall conversion in the question fails.
Long result = Integer.valueOf(-1);
won't work either.
If you do:
Long result = -1L;
it's equivalent (again because of auto-boxing) to:
Long result = Long.valueOf(-1L);
Note that the L makes it a long literal.
First of all, check you Java version: run "java -version" from command line.
In Java version prior to 1.5, the correct syntax is:
Long result = new Long(1L);
In java >1.5 it's done automatically. It's called "autoboxing".
The uppercase "L" tells Java that the number should be interpreted as long.
Long result = -1L;
Long result = (long) -1;
Long result
= Long.valueOf(-1); // this is what auto-boxing does under the hood
How it happened in the first place is that the literal -1 is an int. As of Java5, you can have it auto-boxed into an Integer, when you use it as on Object, but it will not be turned into a Long (only longs are).
Note that an int is automatically widened into a long when needed:
long result = -1; // this is okay
The opposite way needs an explicit cast:
int number = (int) result;
Long result = -1L;
-1 is a primitive int value, Long however is the wrapper class java.lang.Long. An primitive integer value cannot be assigned to an Long object. There are two ways to solve this.
Change the type to long
If you change the type from the wrapper class Long to the primitive type long java automatically casts the integer value to a long value.
long result = -1;
Change the int value to long
If you change the integer value from -1 to -1L to explicit change it to an long literal java can use this primitive value to automatically wrap this value to an java.lang.Long object.
Long result = -1L;
To specify a constant of type long, suffix it with an L:
Long result = -1L;
Without the L, the interpreter types the constant number as an int, which is not the same type as a long.

Categories

Resources