Also suppose you had several overloaded methods that took particular combinations of Dessert parameters:
void moorge(Dessert d, Scone s) { / first form / }
void moorge(Cake c, Dessert d) { / second form / }
void moorge(ChocolateCake cc, Scone s) { / third form / }
void moorge(Dessert... desserts) { / fourth form / }
Now consider the following invocations of moorge:
moorge(dessertRef, sconeRef);
moorge(chocolateCakeRef, dessertRef);
moorge(chocolateCakeRef, butteredSconeRef);
moorge(cakeRef, sconeRef); // INVALID
moorge(sconeRef, cakeRef);
All of these invocations might appear to match the fourth form, and indeed without the other overloaded
forms they would. But the fourth form, being a method that takes a variable number of arguments, will only
be considered by the compiler if it fails to find any candidate methods that explicitly declare two parameters
of assignable type.
The first invocation uses the first form of moorge because the parameter and argument types match exactly.
The second invocation uses the second form because it is the only form for which the provided arguments can
be assigned to the parameter types. In both cases, the method to invoke is clear after step 2 in the
method-matching algorithm.
The third invocation requires more thought. The list of potential overloads includes all three two-argument
forms, because a ChocolateCake reference is assignable to any of the first parameter types, a
ButteredScone reference is assignable to either of the second parameter types, and none of the signatures
matches exactly. So after step 2, you have a set of three candidate methods.
Step 3 requires you to eliminate less specific methods from the set. In this case, the first form is removed from
the set because the third form is more specifica ChocolateCake reference can be assigned to the first
form's Dessert parameter and a Scone reference can be assigned to the first form's Scone parameter, so
the first form is less specific. The second form is removed from the set in a similar manner. After this, the set
of possible methods has been reduced to onethe third form of moorgeand that method form will be invoked.
The fourth invocation is invalid. After step 2, the set of possible matches includes the first and second forms.
Because neither form's parameters are assignable to the other, neither form can be removed from the set in
step 3. Therefore, you have an ambiguous invocation that cannot be resolved by the compiler, and so it is an
invalid invocation of moorge. You can resolve the ambiguity by explicitly casting one of the arguments to
the Dessert type: If you cast cakeRef, then the first form is invoked; if you cast sconeRef, then the
second form is invoked.
The final invocation uses the fourth form. There are no potential candidates after the first phase of step 1, and
no boxing conversions to consider, so the compiler now considers the fourth form in phase 3. An invocation
with two arguments is compatible with a parameter list that has a single sequence parameter, and the types of
the two arguments are assignable to the type of the sequence, so it matches. Because this is the only match
after step 1, it is the method chosen.
These rules also apply to the primitive types. An int, for example, can be assigned to a float, and
resolving an overloaded invocation will take that into account just as it considered that a ButteredScone
reference was assignable to a Scone reference. However, implicit integer conversions to smaller types are