Java 基元和重载

Java primitives and overloading

我一直听说(并认为)Java 是一种强类型语言。但直到最近我才注意到我几乎每天都在使用的东西:intdouble 重载。

我可以写下面的,而且是有效的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 如何进行运算符重载(即重载发生在 + 运算符中,而不是从 intdouble),但我不确定.

希望 SO 能帮我解决这个问题。

int -> double 转换是扩大转换。扩大转换不会丢失数据,因此它们是 performed automatically.

那是因为基元不适用于泛型。他们需要装箱。

对于调用

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

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.

表达式 0 的类型,整数文字,是 int。松散调用上下文定义为

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

intDouble 不受任何这些支持。

如果你有

public static void main(String[] args) throws Exception {
    method(3);
}

public static void method(double d) {
}

它会起作用。

您正在寻找 exciting section 5.2 of the Java Language specification

基本上,当您添加一个 int 和 double 时,它​​会执行扩大转换。但是在尝试将 int 自动装箱为 Double 时,它​​不知道要执行此操作。事实上,这是明确不允许的。

Java不支持运算符重载(String concat(+)除外)运算符。

double k = i + j;

这里发生的事情是隐式的 casting.A 较小的数据类型被扩展为较大的数据类型。这是由 JVM 隐式完成的。

而对于 getOrDefault,原语无法与 generics.And 一起使用 autoboxing。 当您调用 getOrDefault(aString, aStringDoubleMap, 0d); 时,0d 将自动装箱为 Double 对象。 但是在第一种情况下,JVM 无法将 0 自动装箱到 Double 对象。

Java 不会隐式执行扩大原始转换(0 到 0d)和装箱转换(double 到 Double)。

勾选这个link

不允许从 int 到 double 的隐式转换,然后装箱到 Double。

0 只能自动装箱为整数。 0d 可以自动装箱为 Double。