Reputation: 8346
I have always heard (and thought of) Java as a strongly typed language. But only recently did I notice something that I have been using almost on a daily basis: int
and double
overloading.
I can write the following, and it is valid Java code:
int i = 1;
double j = 1.5;
double k = i + j;
But, if I have a method, one of whose arguments is a double
, I need to specify it:
public static <K, V> V getOrDefault(K k, Map<K, V> fromMap, V defaultvalue) {
V v = fromMap.get(k);
return (v == null) ? defaultvalue : v;
}
When I call the above method on a Map<String, Double>
, the defaultvalue
argument cannot be an int
:
getOrDefault(aString, aStringDoubleMap, 0); // won't compile
getOrDefault(aString, aStringDoubleMap, 0d); // compiles and runs just fine
Why does Java overload an int
to double
(just like it does in addition), and then autobox it to Double
? I think the answer lies in how Java does operator overloading (i.e. the overloading happens in the +
operator, and not from int
to double
), but I am not sure.
Here's hoping that SO can help me out on this.
Upvotes: 4
Views: 171
Reputation: 279930
That's because primitives don't work with generics. They need to be boxed.
For the invocation
getOrDefault(aString, aStringDoubleMap, 0); // won't compile
to work, Java would have to box the 0
to an Integer
, then somehow convert that to a Double
. That's not allowed by the language. It's similar to why you can't do
Double value = 3; // Type mismatch: cannot convert from int to Double
From the JLS, on invocation contexts
If the type of the expression cannot be converted to the type of the parameter by a conversion permitted in a loose invocation context, then a compile-time error occurs.
The type of the expression, 0
, an integer literal, is int
. Loose invocation contexts are defined as
Loose invocation contexts allow a more permissive set of conversions, because they are only used for a particular invocation if no applicable declaration can be found using strict invocation contexts. Loose invocation contexts allow the use of one of the following:
- an identity conversion (§5.1.1)
- a widening primitive conversion (§5.1.2)
- a widening reference conversion (§5.1.5)
- a boxing conversion (§5.1.7) optionally followed by widening reference conversion
- an unboxing conversion (§5.1.8) optionally followed by a widening primitive conversion
int
to Double
is not supported by any of those.
If you simply had
public static void main(String[] args) throws Exception {
method(3);
}
public static void method(double d) {
}
it would work.
Upvotes: 4
Reputation: 3274
Java does not support operator overloading(An exception is String concat (+)) operator.
double k = i + j;
Here what is happening is implicit casting.A data type of lower size is widened to a data type of higher size. This is done implicitly by the JVM.
And for getOrDefault
, primitives wont work with generics.And here comes autoboxing
.
When you call getOrDefault(aString, aStringDoubleMap, 0d);
, the 0d will be autoboxed to Double object.
But JVM cannot autobox 0 to a Double object in your first case.
Java will not perform a widening primitive conversion (0 to 0d) and a boxing conversion (double to Double) implicitly.
Check this link
An implicit cast from int to double, followed by boxing to Double, is not allowed.
0 can be only autoboxed to Integer. 0d can be autoboxed to Double.
Upvotes: 1
Reputation: 681
You're looking for the exciting section 5.2 of the Java Language specification.
Basically when you add an int and double it performs a widening conversion. But it doesn't know to do this when trying to Autobox an int to a Double. It's explicitly disallowed in fact.
Upvotes: 3
Reputation: 12213
The int
-> double
conversion is a widening conversion. Widening conversions do not lose data, so they are performed automatically.
Upvotes: -1