Robin Bruegger
Robin Bruegger

Reputation: 255

Java overloading method selection

I'm trying to get my head round how Java selects which method is executed:

//Example 1 prints Square:add(Figure)
Figure fs = new Square();
fs.add(fs);

//Example 2 prints Square:add(Figure)
Rectangle rs = new Square();
rs.add(fs);

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

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

class Figure
{
    public void add(Figure f){ System.out.println("Figure:add(Figure)"); }
}

class Rectangle extends Figure
{
    @Override
    public void add(Figure f){ System.out.println("Rectangle:add(Figure)"); }
    public void add(Rectangle r){ System.out.println("Rectangle:add(Rectangle)"); }
}

class Square extends Rectangle
{
    @Override
    public void add(Figure f){ System.out.println("Square:add(Figure)"); }
    public void add(Square s){ System.out.println("Square:add(Square)"); }
}

What I've learned here is

Based on that, the result of the first two calls is as expected. However, I don't understand the result of example 3 and 4.

It seems to be specified in the java language specification, but I don't understand it.

Upvotes: 17

Views: 5355

Answers (2)

JB Nizet
JB Nizet

Reputation: 691625

rs.add(new Square());

The declared type of rs is Rectangle. So it looks at a method in Rectangle and all superclasses taking a Square or a type compatible with Square as argument. The most specific one is add(Rectangle) since Square is a Rectangle, and since Rectangle is more specific than Figure.

Square ss = new Square();
ss.add(rs);

Looks for a method add(Rectangle) in Square and all super-classes. Rectangle.add(Rectangle) is chosen, since Square.add(Square) doesn't apply (a Rectangle is not a Square), and Square.add(Figure) is less specific.

Upvotes: 4

Jon Skeet
Jon Skeet

Reputation: 1499770

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.

Upvotes: 20

Related Questions