Ответ 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
.
Дайте мне знать, если что-то здесь запутано - если вы можете быть конкретным в отношении какой части, это было бы здорово.