Overloaded Method Selection Based on the Parameter'S Real Type

Overloaded method selection based on the parameter's real type

I expect the method selection to take
in consideration the real (not the
declared) parameter type. Am I missing
something?

Yes. Your expectation is wrong. In Java, dynamic method dispatch happens only for the object the method is called on, not for the parameter types of overloaded methods.

Citing the Java Language Specification:

When a method is invoked (§15.12), the
number of actual arguments (and any
explicit type arguments) and the
compile-time types of the arguments

are used, at compile time, to
determine the signature of the method
that will be invoked (§15.12.2). If
the method that is to be invoked is an
instance method, the actual method to
be invoked will be determined at run
time, using dynamic method lookup
(§15.12.4).

Java overloading method selection

However, I don't understand the result of example 3 and 4.

Okay, let's look at them individually.

Example 3

//Example 3 prints Rectangle:add(Rectangle). Expected Square:add(Square)
rs.add(new Square());

The important parts are the compile-time types of the expressions rs and new Square().

rs is only declared as Rectangle, so the compiler will look at the methods declared by Rectangle and its superclasses:

public void add(Figure f)
public void add(Rectangle r)

The type of the expression new Square() is Square, so both methods are applicable - but the second one is more specific.

So the compiler will call add(Rectangle) on the object that rs refers to. That's it for the compile-time side.

At execution time, the value of rs refers to an instance of Square - but Square doesn't override add(Rectangle) so the method picked is the implementation in Rectangle:

public void add(Rectangle r){ System.out.println("Rectangle:add(Rectangle)"); }

Example 4

//Example 4 prints Rectangle:add(Rectangle). Expected Square:add(Figure)
Square ss = new Square();
ss.add(rs);

Again, let's consider the compile-time types involved... ss is of type Square, and rs is of type Rectangle (compile-time types, remember).

The methods declared by Square and its superclasses are:

public void add(Figure f)
public void add(Rectangle r)
public void add(Square s)

As the compile-time type of rs is only Rectangle (not Square), the first two methods are applicable, but the third isn't. Therefore, again, add(Rectangle) is picked at compile time (as it's more specific than add(Figure)).

Again, the execution time type of ss is Square, which doesn't override add(Rectangle), so the implementation in Rectangle is used.

Let me know if anything in here is confusing - if you can be specific about which part, that would be great.

Overload method with subclasses as parameters but have the method called as the superclass

Yes instanceof and getclass would make your class dependent upon specific types and using such practices are not too good.

But by having methods accepting Dog or Cat you again end up depending upon the concrete specific implementation.

Better way is Define method in Animal interface performAction() . Provide implementation in both Dog and Cat. Now iterate your List of Animals and just invoke performAction() and polymorphically as per actual instance either Dog or Cat implemented method would be called. Later even if there is another implementation of Animal added in code you need not modify your code.

This way you would be having Dog related logic in Dog class, Cat related logic in Cat class and not in any outside different class. This will help in respecting OCP and SRP (Open close and Single Responsibility principles).

Also in case you wish to perform behavior later and your case is such that you know your implementations are fixed and want the using code to inject behavior later then I would recommend Visitor pattern, but still I feel this should not be abused, (design pattern are often not used but abused) sometime we force a pattern when not needed. Visitor pattern uses polymorphic to dispatch the first call to specific instance from where second dispatch calls the overloaded methods in class implementing the Visitor interface. (name can be different, I have mentioned Visitor to imply class implementing the concept of visitor) . Go for Visitor pattern like implementation only if needed and normal above polymorphic based approach do not fit the situation.

Can you dynamically bind overloaded methods?

I mean, the app knows at runtime that it's supposed to call the constructor with an Integer-type argument, it just doesn't know to call the right constructor, after all...

No, it doesn't.

This method:

public <T> B bWith(T it){return new B(it);}

has to be able to work for any parameter: the compiler has to select a single constructor to invoke in that method. The only constructor which meets that criterion there is the Object one.

The only way you can make it do differently at runtime is with explicit casting (you may as well drop the type parameter, it is redundant):

public B bWith(Object it){
if (it == null || it instanceof Integer) {
return new B((Integer) it);
}
return new B(it);
}

Edit: added the it == null check, since new B(null) would actually invoke the Integer constructor, as it is the more specific of the two.

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.

Method overloading with variable argument

This is because the compiler always makes the choice to use the most specific method.

Since your second invocation has two arguments, and an int can be converted to a double without loss of precision (see JLS, section 5.1.2), the compiler elects to call your two-argument method.

An IDE will warn you here about the implicit int to double conversion.


edit: as @OlegEterkhin mentions in comments, see JLS, section 15.2.2 for the process used by the compiler to select which method will be used.

And no, this won't work:

int x = sum(10, 20);

Java method overload choice

Nope, for me this calls the second method as expected!

In the case where you want one to be called over the other, then the hint you'd give is usually a cast to the exact parameter type expected by the method (this cast will work even on null.) So using (Object)arr would force the first method to be called, but by default it will definitely call the second method (on all correct versions of the JVM anyway - contrary to popular belief, this scenario is covered explicitly in the JLS and thus is not ambiguous.)



Related Topics



Leave a reply



Submit