How to Call the More Specific Method of Overloading

How does java compiler most specific method calling rule work?

Java will always try to use the most specific version of a method that is available.

The two methods

public void show(Object o) { ... }

public void show(String s) { ... }

could take null as a valid value. In this scenario the overload taking a String parameter is used, because String is more specific than Object.

If you add a third method

public void show(Integer t) { ... }

your code wouldn't compile any more because String and Integer are not related. Neither is more specific than the other and the compile is not able to decide which one to use.

Java Class.cast to most specific with method overloading


function a(Object m) {}

function a(BasicDbObject) {}

When methods are overloaded, it may not be intuitive to know the method which gets invoked for any set of parameters because, unlike the situation with overridden methods, the method overloading that gets invoked is determined at compile time (i.e. statically) rather than at run time (i.e. dynamically). This behavior is confusing because overriding methods is more common and this sets our expectations for method invocation.

There are some rules for doing method overloading as robustly and as simply as possible. These are all nicely enumerated in Effective Java (J. Bloch, 2nd and 3rd eds.).

Your situation is made complex because:

  • You have two overloadings with the same number of parameters whose types are not radically different ... and ...
  • The behavior of the overloadings is apparently dependent on the type of the parameter (if the behavior was identical, then you simply have one overloading forward to the other)

When this situation arises, you should try to correct it by giving the overloadings different names. It should always be possible to do this and doing so often improves the clarity and maintainability of the code.

If this can't be done for any reason, then the best workaround is to replace the overloadings with a method that accepts the most general parameter type and which invokes helper methods based on the most specific type of the passed argument.

So instead of the above, you can get the behavior you want by using...

public Function a(Object m) {

if (m instanceof BasicDbObject) return doDbObject(m);
if (m instanceof OtherDbObject) return doOtherDbObject(m);

return doGenericObject(m);
}

Note that this isn't the code that you would use when Java adopts pattern matching in the language. Note also that the effect of this code is to give your overloadings different names, but the selection of the distinct method is made at run time using instanceof comparisons rather than at compile time by simply using a distinct name.

TLDR; if you are doing method overloading in a circumstance in which the parameter types are not (or may not be) radically different then you are better off not overloading and using distinct method names.

Method overloading and choosing the most specific type

Java uses early binding. The most specific method is chosen at compile time. The most specific method is chosen by number of parameters and type of parameters. Number of parameters is not relevant in this case. This leaves us with the type of parameters.

What type do the parameters have? Both parameters are expressions, using the ternary conditional operator. The question reduces to: What type does the conditional ternary operator return? The type is computed at compile time.

Given are the two expressions:

(10%2==0)? null : new Object(); // A
(10%2==0)? null : null; // B

The rules of type evaluation are listed here. In B it is easy, both terms are exactly the same: null will be returned (whatever type that may be) (JLS: "If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression."). In A the second term is from a specific class. As this is more specific and null can be substituted for an object of class Object the type of the whole expression is Object (JLS: "If one of the second and third operands is of the null type and the type of the other is a reference type, then the type of the conditional expression is that reference type.").

After the type evaluation of the expressions the method selection is as expected.

The example with if you give is different: You call the methods with objects of two different types. The ternary conditional operator always is evaluated to one type at compile time that fits both terms.

Is it possible to get c# to use method overload of most specific type rather than base type?

This code reminds me of the Haddocks' Eyes poem:

But I was thinking of a plan

 To dye one's whiskers green,

And always use so large a fan

 That they could not be seen.

First, your code creates a subclass, but then it casts the object back to the base class, so the compiler cannot see the actual type! The overload in your example is resolved at compile time, so the Compose(BaseClass item) will be called.

You could turn things around and have .NET resolve the overload dynamically for you by hiding all overloads, and exposing a single method that takes a base class and performs a dynamic cast:

public class Processor {
public string Compose(BaseClass item) {
return ComposeImpl((dynamic)item);
}
private string ComposeImpl(BaseClass item) {
return item.Foo;
}
private string ComposeImpl(DerivedClass item) {
return ComposeImpl((BaseClass)item) + item.Bar;
}
}

The "magic" is on this line:

return ComposeImpl((dynamic)item);

the item is cast to dynamic, which tells the system that the actual overload of ComposeImpl must be picked based on the run-time type of the BaseClass object.



Related Topics



Leave a reply



Submit