C#和Java中的方法重载
我在C#中运行了以下方法。
public float Add(float num1, long num2) { Console.WriteLine("method 1"); return 0; } public float Add(int num1, float num2) { Console.WriteLine("method 2"); return 0; }
在这里,如果我调用Add(1,1)
,它会给出歧义。 现在让我在第一个方法中交换float
和long
位置,如下所示:
public float Add(long num1, float num2) { Console.WriteLine("method 1"); return 0; } public float Add(int num1, float num2) { Console.WriteLine("method 2"); return 0; }
现在它打印“方法2”作为输出。 第一种情况模棱两可的原因是什么?
如果我在我的代码中编写以下两种方法:
public float Add(long num1, long num2) { Console.WriteLine("method 1"); return 0; } public float Add(int num1, float num2) { Console.WriteLine("method 2"); return 0; }
在调用Add(1,1)
,它会产生歧义错误。 为什么它不是最佳匹配,这是第二种方法(有int
和float
)? 根据我的说法,它应该将方法2作为输出。
从C#规范 , 7.5.3.2 Better Function成员 :
给定一个参数列表A,其中包含一组参数表达式{E1,E2,…,EN}和两个适用的函数成员MP和MQ,参数类型为{P1,P2,…,PN}和{Q1,Q2, …,QN},MP被定义为比MQ更好的函数成员
- 对于每个参数,从EX到QX的隐式转换并不比从EX到PX的隐式转换更好
- 对于至少一个参数,从EX到PX的转换优于从EX到QX的转换。
请注意,规范使用术语隐式转换 ,其方式包括身份转换。 请参见6.1隐式转换 :
以下转化归类为隐式转化:
- 身份转换
- 隐式数字转换
- […]
从6.1.1身份转换 :
身份转换从任何类型转换为相同类型。 […]
您的参数类型集是:
{ int, int }
在第一种情况下,候选人是:
{ float, long } { int, float }
让MP成为第一个候选者,MQ成为第二个候选者。
- 有一个参数X(= 1),其中从EX到QX的隐式转换优于从EX到PX的隐式转换:
int
到int
优于int
到float
(因为它是相同的类型)。
让MP成为第二个候选者,MQ成为第一个候选者。
- 有一个参数X(= 2),其中从EX到QX的隐式转换优于从EX到PX的隐式转换:
int
tolong
优于int
tofloat
。
两个候选人都不满足第一个子弹。 这里描述的打破平局机制在这里不适用(因为这两种方法都不是通用的,也不是可变参数,也没有可选参数等)。 因此,这种呼吁是模棱两可的。
在第二种情况下,候选人是:
{ long, float } { int, float }
让MP成为第一个候选者,MQ成为第二个候选者。
- 有一个参数X(= 1),其中从EX到QX的隐式转换优于从EX到PX的隐式转换:
int
到int
优于int
到long
(因为它是相同的类型)。
让MP成为第二个候选者,MQ成为第一个候选者。
- 没有参数X,从EX到QX的隐式转换优于从EX到PX的隐式转换。
- 有一个参数X(= 1),其中从EX到PX的隐式转换优于从EX到QX的转换:
int
到int
优于int
到long
。
由于第二个候选人满足两个子弹,因此它比第一个更好。
在第三种情况下,候选人是:
{ long, long } { int, float }
就像第一种情况一样:
-
int
到int
比int
更好。 - 但
int
tolong
比int
更好float
。
因此,呼叫再次模棱两可。
Java语言规范在15.12.2.5中说明选择最具体的方法 :
一个适用的方法m1比另一个适用的方法m2更具体,用于参数表达式e1,…,ek的调用,如果满足以下任何条件:
- m2是通用的,并且根据§18.5.4推断m1对于参数表达式e1,…,ek比m2更具体。
- m2不是通用的,m1和m2适用于严格或松散的调用,并且m1具有forms参数类型S1,…,Sn和m2具有forms参数类型T1,…,Tn,类型Si更多对于所有i,参数ei的特定于Ti(1≤i≤n,n = k)。
- m2不是通用的,m1和m2适用于变量arity调用,并且m1的前k个变量arity参数类型是S1,…,Sk和m2的前k个变量arity参数类型是T1,..对于所有i(1≤i≤k),对于参数ei,Si类型比Ti更具特异性。 另外,如果m2具有k + 1个参数,则m1的第k + 1个可变参数类型是第m + 1个可变参数类型m2的子类型。
上述条件是一种方法可能比另一种方法更具体的唯一情况。
如果S <:T(§4.10),则类型S对于任何表达式比类型T更具体。
和以前一样,请注意这里描述的关系包括S和T是相同类型的情况,而不是彼此的严格子类型 (这将是正确的子类型 )。
对于原始类型,在4.10.1原始类型之间的子类型中描述:
以下规则定义了基本类型之间的直接超类型关系:
- 双> 1浮动
- 浮> 1长
- 长> 1 int
- int> 1个字符
- int> 1短
- 短> 1个字节
有了这些,对于这种特殊情况,重载决策规则实际上与C#相同。 前面的解释是适用的。
在这种情况下,编译器会查找与输入变量最匹配的方法
public float Add(float num1, long num2) { Console.WriteLine("method 1"); return 0; } public float Add(int num1, float num2) { Console.WriteLine("method 2"); return 0; }
添加(1,1)这个方法不清楚应该选择哪个版本,所以为了解决这个问题(第一个参数中的float或int,long或第二个中的float)你必须在方法中指定一个输入参数的类型
Add(1F, 1); Add(1, 1F);
但是在下面的代码中,编译器将int类型视为第一个方法参数中数字1的最接近的matche
public float Add(long num1, float num2) { Console.WriteLine("method 1"); return 0; } public float Add(int num1, float num2) { Console.WriteLine("method 2"); return 0; }
所以如果你想调用第一种方法,你应该这样做
Add(1L, 1);
并为您的第三部分代码
public float Add(long num1, long num2) { Console.WriteLine("method 1"); return 0; } public float Add(int num1, float num2) { Console.WriteLine("method 2"); return 0; }
再次不清楚哪个版本编译器应该使用,因为你的方法中的第二个输入参数(float或long),所以为了解决这个问题,你必须指定输入
Add(1L, 1);//method 1 Add(1, 1F);// method 2