我一直听说(并且认为)Java是一种强类型语言。但直到最近我才注意到我几乎每天都在使用的内容:int
和double
重载。
我可以编写以下内容,它是有效的Java代码:
int i = 1;
double j = 1.5;
double k = i + j;
但是,如果我有一个方法,其中一个参数是double
,我需要指定它:
public static <K, V> V getOrDefault(K k, Map<K, V> fromMap, V defaultvalue) {
V v = fromMap.get(k);
return (v == null) ? defaultvalue : v;
}
当我在Map<String, Double>
上调用上述方法时,defaultvalue
参数不能是int
:
getOrDefault(aString, aStringDoubleMap, 0); // won't compile
getOrDefault(aString, aStringDoubleMap, 0d); // compiles and runs just fine
为什么Java会将int
重载为double
(就像它一样),然后将其自动装箱到Double
?我认为答案在于Java如何进行运算符重载(即重载发生在+
运算符中,而不是从int
发生到double
),但我不确定。
希望SO可以帮助我解决这个问题。
答案 0 :(得分:4)
那是因为原语不适用于泛型。他们需要装箱。
对于调用
getOrDefault(aString, aStringDoubleMap, 0); // won't compile
要工作,Java必须将0
打包到Integer
,然后以某种方式将其转换为Double
。语言不允许这样做。它类似于你不能做的原因
Double value = 3; // Type mismatch: cannot convert from int to Double
来自JLS,on invocation contexts
如果表达式的类型无法转换为类型 参数通过松散调用上下文中允许的转换, 然后发生编译时错误。
表达式的类型0
是一个整数文字,是int
。松散的调用上下文定义为
松散的调用上下文允许更宽松的转换集, 因为如果没有,它们仅用于特定的调用 可以使用严格的调用上下文找到适用的声明。 松散的调用上下文允许使用以下之一:
- 身份转换(第5.1.1节)
- 扩大原始转换(第5.1.2节)
- 扩大参考转换(第5.1.5节)
- 拳击转换(§5.1.7),可选地后跟加宽参考转换
- 一个拆箱转换(第5.1.8节),可选地后跟一个加宽的基元转换
int
至Double
不受任何支持。
如果你只是
public static void main(String[] args) throws Exception {
method(3);
}
public static void method(double d) {
}
它会起作用。
答案 1 :(得分:3)
您正在寻找exciting section 5.2 of the Java Language specification。
基本上,当你添加一个int和double时,它会执行加宽转换。但是当尝试将一个int Autobox转换为Double时,它不知道这样做。事实上它是明确禁止的。
答案 2 :(得分:1)
Java不支持运算符重载(一个例外是String concat(+))运算符。
double k = i + j;
这里发生的是隐式转换。较小尺寸的数据类型被扩展为更大尺寸的数据类型。这是由JVM隐式完成的。
对于getOrDefault
,原语不适用于泛型。这里有autoboxing
。
当您调用getOrDefault(aString, aStringDoubleMap, 0d);
时,0d将自动装箱为Double对象。
但是在第一种情况下,JVM无法将0自动装箱到Double对象。
Java不会隐式执行扩展原始转换(0到0d)和装箱转换(double到Double)。
选中此link
不允许从int到double的隐式转换,然后装箱到Double。
0只能自动装箱到整数。 0d可以自动装箱为Double。
答案 3 :(得分:-1)
int
- &gt; double
转化是一种不断扩大的转化。扩展转化不会丢失数据,因此它们为performed automatically。