Материал предоставлен https://it.rfei.ru

Концепции ООП

Пример в этом разделе демонстрирует основные концепции ООП, в последующих разделах главы концепции, представленные здесь, рассматриваются более подробно.

На рис.6-2 показано отношения наследование между классом String и его суперклассом Object. Клиент, который использует объект типа String, определяется в примере 6.2. Во время выполнения метода main() на созданной в нем строке (1) объект класса String указывают две ссылки: ссылка stringRef подкласса String и сслыкa objRef суперкласса Object. Изучая код метода main(), познакомьтесь с особенностями ООП.

Рис. 6.2. Отношение наследования между классами String и Object

Пример 6.2. Иллюстрация наследования

// String подкласс Object
class Client {
    public static void main(String[] args) {

        String stringRef = new String("Java");                    // (1)

        System.out.println("(2): " + stringRef.getClass());       // (2)
        System.out.println("(3): " + stringRef.length());         // (3)
        Object objRef = stringRef;                                // (4)
   //   System.out.println("(5): " + objRef.length());            // (5) не верно.
        System.out.println("(6): " + objRef.equals("Java"));      // (6)
        System.out.println("(7): " + objRef.getClass());          // (7)

        stringRef = (String) objRef;                              // (8)
        System.out.println("(9): " + stringRef.equals("C++"));    // (9)
    }
}

Вывод программы:

(2): class java.lang.String
(3): 4
(6): true
(7): class java.lang.String
(9): false

Наследование от суперкласса

Подкласс String наследует метод getClass() от суперкласса Object. Клиент класса String может напрямую вызывать этот унаследованный метод в объектах класса String таким же образом, как если бы метод был определен в самом классе String. В примере 6.2 это показано в строке (2).

System.out.println("(2): " + stringRef.getClass());     // (2)

Расширение суперкласса

Подкласс String определяет метод length(), которого нет в суперклассе Object, таким способом расширяет суперкласс. В примере 6.2 вызов этого нового метода объекта показан в строке (3).

System.out.println("(3): " + stringRef.length());       // (3)

Восходящее преобразование

Ссылка на подкласс может быть присвоена ссылке на суперкласс, потому что объект подкласса может использоваться везде, где может использоваться объект суперкласса. Это называется восходящим преобразованием, так как ссылка присваивается на элемент выше в иерархии наследования. В примере 6.2 это показано в строке (4), в которой значение ссылки подкласса stringRef присваивается ссылке суперкласса objRef.

Object objRef = stringRef;                              // (4)

Обе ссылки указывают на один и тот же объект string после присваивания. Теперь может показаться привлекательным вызов методов подкласса String через ссылку суперкласса objRef, как показано в строке (5).

System.out.println("(5): " + objRef.length());          // (5) Неверно.

Однако это не будет работать, потому что компилятор не знает, на какой объект указывает ссылка objRef. Он знает только тип ссылки. Так как в объявлении класса Object нет метода с именем length(), вызов метода length() в строке (5) будет отмечен компилятором как ошибка.

Переопределение метода

В противоположность ситуации в строке (5) вызов метода equals() в строке (6) через ссылку суперкласса objRef допустим, потому что компилятор может проверить, что в классе Object определен метод equals().

System.out.println("(6): " + objRef.equals("Java"));    // (6)

Обратите внимание, что этот метод подменяется в классе String на метод с такой же сигнатурой (т.е. имя метода и параметры) и таким же возвращаемым типом. Это называется Реопредепением метода.

Полиморфизм и динамическое связывание методов

Во время выполнения вызов метода equals() в строке (6) через ссылку суперкласса objRef не обязательно приведет к вызову метода equals() из класса Object. Вызываемый метод зависит от типа реального объекта, на который указывает ссылка, во время выполнения. Реальный метод находится при помощи механизма динамического поиска метода. Способность ссылки суперкласса обозначать объекты своего собственного класса и его подклассов на этапе выполнения называется полиморфизмом.

В случае нормального выполнения программы ссылка objRef будет ссылаться на объект класса String в строке (6), и в результате будет выполнен метод equals() класса String, а не одноименный метод из класса Object.

Ситуация в строке (7), в которой метод getclass() вызывается через ссылку суперкласса objRef, допустима, потому что метод getClass определяется в классе Object.

System.out.println("(7): " + objRef.getClass());        // (7)

В этом случае при нормальном ходе выполнения программы ссылка objRef будет ссылаться на объект класса String в строке (7). Динамический поиск метода определит, какая реализация соответствует сигнатуре метода getClass(). Поскольку ни один метод getClass() не определен в классе String, выполняется метод getClass(), унаследованный от объекта Object.

Нисходящее преобразование

Преобразование типа значения ссылки суперкласса к типу подкласса называется нисходящим преобразованием. Это показано в примере 6.2 с помощью операции присваивания, которая требует явного приведения типа.

tringRef = (String) objRef;                            // (8)
System.out.println("(9): " + stringRef.equals("C++"));  // (9)

В строке (8) исходная ссылка objRef имеет тип Object, который является суперклассом для класса ссылки назначения stringRef. Если ссылка objRef реально обозначала объект класса String во время выполнения, то механизм приведения преобразует ее в правильный тип, так что присваивание ее ссылке stringRef допустимо в строке (8). Ссылка stringRef может потом использоваться для вызова метода equals() объекта String, как показано в строке (9). Не удивляйтесь, что будет выполнен метод equals() из класса String.

Компилятор проверяет, что существует отношение наследования между исходным типом ссылки и типом ссылки, определяемым в приведении. Однако преобразование может оказаться невозможным на этапе выполнения. Если во время выполнения ссылка objRef обозначает объект класса Object или какой-то не имеющий отношения к делу подкласс класса Object, то, очевидно, преобразование значения этого типа к ссылке подкласса String недопустимо. В этом случае во время выполнения будет выброшено исключение ClassCastException. Для выяснения типа объекта перед тем, как к нему будут применены какие-либо преобразования, может использоваться оператор instanceof.

Одиночное наследование реализацииПереопределение и сокрытие методов