Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Method overloading in C# and Java

I ran the following methods in 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;
}

Here, if I call Add(1,1), it gives ambiguity. Now let me swap position of float and long in the first method as follows:

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;
}

Now it prints "method 2" as output. What is the reason behind the ambiguity in first case?

And if I write following two methods in my code:

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;
}

On calling Add(1,1), it gives ambiguity error. Why does it not go for best match, which is the second method (having int and float)? According to me, it should have given method 2 as output.

like image 682
Gaurav Jain Avatar asked Sep 05 '15 14:09

Gaurav Jain


2 Answers

From the C# specification, 7.5.3.2 Better Function member:

Given an argument list A with a set of argument expressions { E1, E2, ..., EN } and two applicable function members MP and MQ with parameter types { P1, P2, ..., PN } and { Q1, Q2, ..., QN }, MP is defined to be a better function member than MQ if

  • for each argument, the implicit conversion from EX to QX is not better than the implicit conversion from EX to PX, and
  • for at least one argument, the conversion from EX to PX is better than the conversion from EX to QX.

Note that the specification uses the term implicit conversion in a way that includes identity conversions. See 6.1 Implicit conversions:

The following conversions are classified as implicit conversions:

  • Identity conversions
  • Implicit numeric conversions
  • [...]

And from 6.1.1 Identity conversion:

An identity conversion converts from any type to the same type. [...]


Your set of argument types is:

{ int, int }

In the first case, the candidates are:

{ float, long } 
{ int, float }

Let MP be the first candidate and MQ be the second candidate.

  • There is one argument X (= 1) where the implicit conversion from EX to QX is better than the implicit conversion from EX to PX: int to int is better than int to float (because it's the same type).

Let MP be the second candidate and MQ be the first candidate.

  • There is one argument X (= 2) where the implicit conversion from EX to QX is better than the implicit conversion from EX to PX: int to long is better than int to float.

Neither candidate satisfies the first bullet. The tie breaking mechanisms that are described the specification are not applicable here (because neither method is generic, neither is variadic, neither has optional parameters, etc). Thus, this call is ambiguous.


In the second case, the candidates are:

{ long, float }
{ int, float }

Let MP be the first candidate and MQ be the second candidate.

  • There is one argument X (= 1) where the implicit conversion from EX to QX is better than the implicit conversion from EX to PX: int to int is better than int to long (because it's the same type).

Let MP be the second candidate and MQ be the first candidate.

  • There is no argument X where the implicit conversion from EX to QX is better than the implicit conversion from EX to PX.
  • There is one argument X (= 1) where the implicit conversion from EX to PX is better than the conversion from EX to QX: int to int is better than int to long.

Since the second candidate satisfies both bullets, it is a better match than the first.


In the third case, the candidates are:

{ long, long }
{ int, float } 

Just like in the first case:

  • int to int is better than int to long.
  • But int to long is better than int to float.

Thus, the call is ambiguous again.


The Java Language Specification states in 15.12.2.5 Choosing the Most Specific Method:

One applicable method m1 is more specific than another applicable method m2, for an invocation with argument expressions e1, ..., ek, if any of the following are true:

  • m2 is generic, and m1 is inferred to be more specific than m2 for argument expressions e1, ..., ek by §18.5.4.
  • m2 is not generic, and m1 and m2 are applicable by strict or loose invocation, and where m1 has formal parameter types S1, ..., Sn and m2 has formal parameter types T1, ..., Tn, the type Si is more specific than Ti for argument ei for all i (1 ≤ i ≤ n, n = k).
  • m2 is not generic, and m1 and m2 are applicable by variable arity invocation, and where the first k variable arity parameter types of m1 are S1, ..., Sk and the first k variable arity parameter types of m2 are T1, ..., Tk, the type Si is more specific than Ti for argument ei for all i (1 ≤i≤k). Additionally, if m2 has k+1 parameters, then the k+1'th variable arity parameter type of m1 is a subtype of the k+1'th variable arity parameter type of m2.

The above conditions are the only circumstances under which one method may be more specific than another.

A type S is more specific than a type T for any expression if S <: T (§4.10).

Like before, please note that the relation described here includes the case where S and T are the same type, not strictly subtype of one another (which would be proper subtyping).

For primitive types, is described in 4.10.1 Subtyping among Primitive Types:

The following rules define the direct supertype relation among the primitive types:

  • double >1 float
  • float >1 long
  • long >1 int
  • int >1 char
  • int >1 short
  • short >1 byte

With these, the overload resolution rules are effectively the same as with C# for this particular case. The previous explanation is applicable.

like image 84
Theodoros Chatzigiannakis Avatar answered Oct 07 '22 10:10

Theodoros Chatzigiannakis


In this case the compiler look for the method which closest matches the input variable(s)

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) this method is not clear which version should be choose, so for solving this problem(float or int in the first parameter, long or float in second one) you have to specify type of one of inputs parameter in method

  Add(1F, 1);
  Add(1, 1F);

But following code ,compiler consider int type as closest matche for number 1 in the first method parameter

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;
}

so If you want to call the first method you should do this

Add(1L, 1);

And for third part of you code

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;
}

again it is not clear which version complier should use, because of second input parameter in your method(float or long), so for solving this issue you have to specify the input

    Add(1L, 1);//method 1
    Add(1, 1F);// method 2
like image 44
Arash Avatar answered Oct 07 '22 09:10

Arash