问题描述
public class Main {
static void over_Var(float a,Character... ab) {
System.out.println("Method1");
}
static void over_Var(Character... ab) {
System.out.println("Method2");
}
public static void main(String[] args) {
System.out.println("Hello World");
over_Var(1,'m');
over_Var('k','m');
}
}
我收到这样的错误:
Main.java:19: error: reference to over_Var is ambiguous
over_Var('k','m');
^
both method over_Var(float,Character...) in Main and method over_Var(Character...) in Main
match 1 error
如果我使用 char
而不是 Character
或删除行 over_Var('k','m');
为什么我会收到这样的错误?
解决方法
为什么 over_Var(1,'m');
没有歧义
1
是一个整数。它不能直接传递给 Character
或 char
类型的参数(没有强制转换),因此唯一的选择是 (float,Character...)
重载。 确实存在 int
到 float
的扩展原始转换,这在 invocation context 中是允许的。
您可能认为可以将 1
转换为 Character
,因为您可以在 assignment contexts 中这样做。
Character a = 1;
然而,这纯粹是因为在 JLS 的“分配上下文”部分(见上面的链接),有这部分开始:
此外,如果表达式是 byte
、short
、char
或 int
类型的常量表达式(第 15.28 节):
[...]
调用上下文部分没有这一段。因此,实际上,赋值上下文给予常量表达式(如 1
)特殊处理,允许将它们转换为比实际更小的类型。调用上下文不这样做。
为什么 over_Var('k','m');
不明确
在这种情况下,两种重载都适用。 'k'
是一个 char
,并且在调用上下文中允许从 char
到 float
的转换(再次扩大原始转换)。在松散的调用上下文中也允许从 char
到 Character
的转换。
如果有多种适用的方法,编译器会选择最具体的一种。哪个更具体?好吧,两个调用的第二个参数都是 Character
,所以我们只需要考虑第一个参数的类型。一个是 float
,另一个是 Character
。其中哪一个更具体取决于它们的子类型关系。根据 subtyping rules,它们是不相关的,因此两者都不是更具体,因此您会收到编译器错误。
这是规范 15.12.2 整个部分的简化,我强烈建议您自行探索 :)
为什么改为 char
有效
与float
和Character
不同,float
和char
实际上是相关的!具体:
-
的超类型double
是float
-
的超类型float
是long
-
的超类型long
是int
-
的超类型int
是char
-
的超类型int
是short
-
的超类型short
是byte
所以 char
是 float
的子类型,所以 over_Var(char...)
比 over_Var(float,char...)
更具体,因此将是首选。
解决歧义的一些方法
要调用 (float,Character...)
,您只需投射:
over_Var((float) 'k','m');
要调用 (Character...)
,您可以传入一个 Character[]
。
over_Var(new Character[] {'k','m'});
,
这里的问题是不是你有可变长度的参数。这是一个 The method X is ambiguous for the type Y
error,在链接中解释:
如果多个成员方法既可访问又适用于方法调用……Java 编程语言使用选择最具体方法的规则。
java编译器不认为其中任何一个更具体,因此方法不明确调用错误
首先……为什么字符和浮点数之间有联系?
这与字符在 Java 中的底层工作方式有关。
示例:
System.out.println(200 - 'a'); // subtracting a char from 200
System.out.println(0 - 'a'); // subtracting a char from 0
应该返回:
103
-97
那么有什么解决办法呢?您指出了一个:将 Character
更改为 char
。但为什么会这样呢?您偶然发现了非常有趣的 Java 行为。以这段代码为例:
public class Main {
static void over_Var(float a,Character b) {
System.out.println("Method1");
}
static void over_Var(char a,Character b) {
System.out.println("Method2");
}
static void over_Var(Character a,Character b) {
System.out.println("Method3");
}
public static void main(String[] args) {
over_Var('k','m');
}
}
-
由于
char
和Character
之间的歧义,此 将有错误。 -
注释掉第一个函数。由于
之间的歧义,它仍然会出现错误char
和Character
-
注释掉第二个函数。 由于
之间的歧义,它会失败float
被转换为Character
和Character
-
注释掉第三个函数。它将打印
Method2
。它知道将char
置于float
之上!
这似乎……违反了传递性。字符 + 字符 = 歧义。字符 + 浮点数 = 歧义。但是 char + float = 无歧义? 我会给你留下“奇怪”这个词。
编辑:这样做的原因(请参阅 Sweeper 的更好答案)是它知道将 char
置于 float
之上,因为它们是相关类型; char 是 float 的子类型。另一方面, Character 是不相关的。 (换句话说,明显缺乏“传递性”的原因是特异性只能在被比较的类型的上下文中进行评估。)