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);
What does this error 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:
where
<type1>
and<type2>
are both primitive numeric types; i.e. one ofbyte
,char
,short
,int
,long
,float
ordouble
.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:
the
sqrt
method produces adouble
, but a conversion fromdouble
toint
is potentially lossy.What does "potentially lossy" mean?
Well lets look at a couple of examples.
A conversion of a
long
to anint
is a potentially lossy conversion because there arelong
values that do not have a correspondingint
value. For example, anylong
value that is greater than 2^31 - 1 is too large to be represented as anint
. Similarly, any number less than -2^31 is too small.A conversion of an
int
to along
is NOT lossy conversion because everyint
value has a correspondinglong
value.A conversion of a
float
to anlong
is a potentially lossy conversion because therefloat
values that are too large or too small to represent aslong
values.A conversion of an
long
to afloat
is NOT lossy conversion because everylong
value has a correspondingfloat
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
tobyte
orchar
char
tobyte
orshort
int
tobyte
,short
orchar
long
tobyte
,short
,char
orint
float
tobyte
,short
,char
,int
orlong
double
tobyte
,short
,char
,int
,long
orfloat
.How do you fix the error?
The way to make the compilation error go away is to add a typecast. For example;
becomes
But is that really a fix? Consider that the square root of
47
is6.8556546004
... butsquareRoot
will get the value6
. (The conversion will truncate, not round.)And what about this?
That results in
b
getting the value0
. 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 of512
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:
<type1>
be a different type, so that a lossy conversion isn't needed here?Some specific cases:
"Possible lossy conversion" when subscripting.
First example:
The problem here is that array index value must be
int
. Sod
has to be converted fromdouble
toint
. 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:
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.
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.