Подтвердить что ты не робот

Выбор метода перегрузки Java

Я пытаюсь понять, как Java выбирает, какой метод выполняется:

//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)"); }
}

То, что я узнал здесь,

  • Подпись метода определяется по типам данных времени компиляции
  • Вызываемый действительный метод зависит от динамического типа объекта, на который вызывается метод.

Исходя из этого, результат первых двух вызовов соответствует ожиданиям. Однако я не понимаю результат из примеров 3 и 4.

Как представляется, в спецификации java language, но я этого не понимаю.

4b9b3361

Ответ 1

Однако я не понимаю результат из примеров 3 и 4.

Хорошо, давайте посмотрим на них индивидуально.

Пример 3

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

Важными частями являются типы времени компиляции выражений rs и new Square().

rs объявляется только как Rectangle, поэтому компилятор будет искать методы, объявленные Rectangle и его суперклассами:

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

Тип выражения new Square() равен Square, поэтому оба метода применимы, но второй более конкретный.

Итак, компилятор вызовет add(Rectangle) для объекта, к которому относится rs. Это для стороны компиляции.

Во время выполнения значение rs относится к экземпляру Square - но Square не переопределяет add(Rectangle), поэтому выбранный метод - это реализация в Rectangle:

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

Пример 4

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

Опять же, рассмотрим задействованные типы времени компиляции... ss имеет тип Square, а rs имеет тип Rectangle (типы времени компиляции, помните).

Способы, объявленные Square и его суперклассами:

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

Поскольку тип rs для компиляции - это только Rectangle (не Square), применяются первые два метода, а третий - нет. Поэтому опять же, add(Rectangle) выбирается во время компиляции (поскольку это более специфично, чем add(Figure)).

Опять же, тип времени выполнения ss равен Square, который не переопределяет add(Rectangle), поэтому используется реализация в Rectangle.

Дайте мне знать, если что-то здесь запутано - если вы можете быть конкретным в отношении какой части, это было бы здорово.

Ответ 2

rs.add(new Square());

Объявленный тип rs - Rectangle. Таким образом, он рассматривает метод в Rectangle и все суперклассы, принимающие квадрат или тип, совместимые с квадратом как аргумент. Наиболее конкретным является add(Rectangle), поскольку Square является прямоугольником, а так как Rectangle более конкретный, чем рисунок.

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

Выбирает метод add(Rectangle) в квадрате и всех суперклассах. Rectangle.add(Rectangle) выбирается, так как Square.add(Square) не применяется (прямоугольник не является квадратом), а Square.add(Figure) менее конкретным.