Java基本数据类型和重载

4

我一直认为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如何进行运算符重载(即重载发生在+运算符中,而不是从int到double),但我不确定。
希望SO能够帮助我解决这个问题。
4个回答

4
那是因为原始类型不能与泛型一起使用。它们需要被装箱。
对于这个调用:
getOrDefault(aString, aStringDoubleMap, 0); // won't compile

要让它工作,Java必须将0装箱为Integer,然后以某种方式将其转换为Double。这在语言上是不允许的。这类似于为什么你不能这样做。
Double value = 3; // Type mismatch: cannot convert from int to Double

来自JLS,调用上下文

如果表达式的类型不能通过在松散调用上下文中允许的转换转换为参数的类型,则会发生编译时错误。

表达式的类型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) {
}

它会工作。


我明白了。我原以为Java会首先将0转换为0d(由Renjith和Andrew提到的扩展转换),然后再进行自动装箱。当然,如你所指出的那样,从IntegerDouble的转换是不允许的。 - Chthonic Project

3
你正在寻找Java语言规范的第5.2节令人兴奋的部分
基本上,当你将int和double相加时,它会执行扩展转换。但是,当尝试将int自动装箱为Double时,它不知道如何做到这一点。事实上,这是明确禁止的。

1
这是由调用上下文而非赋值上下文所覆盖的。 - Sotirios Delimanolis
这是正确的,它是5.3节而不是5.2节,但是两个部分允许转换的列表是相同的。 - Andrew Aitken

1

Java 不支持运算符重载(一个例外是字符串连接 (+) 运算符)。

double k = i + j;

这里发生的是隐式转换,低位数据类型扩展为高位数据类型。JVM会自动进行这个操作。
对于getOrDefault,原始类型无法与泛型一起使用。这时就需要使用autoboxing了。 当您调用getOrDefault(aString, aStringDoubleMap, 0d);时,0d将被autoboxed为Double对象。 但是JVM无法在第一个情况下将0 autoboxed为Double对象。
Java不会隐式执行扩展原始转换(从0到0d)和装箱转换(从double到Double)。
请查看此link
从int到double的隐式转换,然后包装成Double是不允许的。
0只能被autoboxed到Integer中。 0d可以被autoboxed到Double中。

-1

intdouble 的转换是一种扩展转换。扩展转换不会丢失数据,因此它们会 自动执行


1
那不是OP的问题。问题是为什么在Double d = 1;中,字面值1没有被转换为double,然后自动装箱为Double。而且,我没有给你的答案投反对票。 - Turing85
也许问题中有错别字?引用:“为什么Java会将int重载为double(就像在加法中一样),然后再自动装箱为Double?”我猜他的意思是“不会”,但我错过了那个。 - dsh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接