ООП в примерах. Часть 2. Наследование

от
Совершенный код   ооп, java me, java

Продолжаем осваивать ООП.
Первая часть

Разберёмся с наследованием.
Наследование позволяет взять доступные свойства родительского (базового) класса и использовать их в производных (потомках).
Сразу пример.
Вернём наш класс пункта меню в первоначальный вид:
  1. public class MenuItem {
  2.  
  3.     private String name;
  4.  
  5.     public MenuItem(String name) {
  6.         this.name = name;
  7.     }
  8.  
  9.     public String getName() {
  10.         return name;
  11.     }
  12. }
Теперь давайте создадим производный класс с цветом пункта меню:
  1. public class ColorMenuItem extends MenuItem {
  2.  
  3.     private int color;
  4.  
  5.     public ColorMenuItem(String name, int color) {
  6.         super(name);
  7.         this.color = color;
  8.     }
  9.  
  10.     public int getColor() {
  11.         return color;
  12.     }
  13. }
Первое, что бросается в глаза это ключевое слово extends. Оно означает, что новый класс будет унаследован от класса MenuItem, то есть будет брать все его свойства.
Второе нововведение это super(name). super означает обращение к родительскому классу (то есть классу MenuItem). В данном случае мы обращаемся к конструктору родительского класса: public MenuItem(String name).
При вызове конструктора public ColorMenuItem(String name, int color) строка name передастся конструктору public MenuItem(String name), затем (уже внутри класса MenuItem) произойдёт присваивание переменной члену класса: this.name = name; После этого выполнение конструктора базового класса выполнится и начнут выполняться следующие операторы уже в нашем классе ColorMenuItem: this.color = color;
Ещё раз, что будет выполнено:
super(name) класса ColorMenuItem
----this.name = name; класса MenuItem
this.color = color; класса ColorMenuItem

Теперь давайте сделаем ещё один производный класс, на этот раз с иконкой:
  1. public class IconMenuItem extends MenuItem {
  2.  
  3.     private Image icon;
  4.  
  5.     public IconMenuItem(String name, String iconName) {
  6.         super(name);
  7.         createIcon(iconName);
  8.     }
  9.  
  10.     public Image getIcon() {
  11.         return icon;
  12.     }
  13.  
  14.     private void createIcon(String iconName) {
  15.         try {
  16.             icon = Image.createImage("/" + iconName);
  17.         } catch (IOException ex) {
  18.             icon = Image.createImage(18, 18);
  19.         }
  20.     }
  21. }
Теперь вызов будет таков:
super(name) класса IconMenuItem
----this.name = name; класса MenuItem
createIcon(iconName) класса IconMenuItem
--icon = Image.createImage("/" + iconName); класса IconMenuItem
--//если такой картинки нет, то будет вызван icon = Image.createImage(18, 18);

Ещё раз напомню, во всех этих трёх классах метод getName() будет доступен, так как он не объявлен как private, а значит распространяется на все производные классы.
Открыть спойлер
А вот наоборот нельзя. Класс MenuItem ничего не знает про своих потомков. Он не может получить доступ к полю color, icon и методам getIcon(), getColor().

  screen.png
Перепишем сам класс меню OopMenu3.java
Обратите внимание, мы можем в массив MenuItem класть элементы производных классов.
Так тоже можно: MenuItem item = new ColorMenuItem("Name", 0x00);
Но мы не сможем получить доступ к методу getColor, надо конкретно указывать тип: ((ColorMenuItem) item).getColor();
А вот так написать мы можем, но работать не будет: ((IconMenuItem) item).getIcon()
Это потому что у нас инициализируется объект ColorMenuItem и мы не можем привести его к другому наследуемому типу.
Получить доступ к методу getName() мы можем из любого класса:
  1. ColorMenuItem cmItem = new ColorMenuItem("Name", 0x00);
  2. cmItem.getName();
  3. IconMenuItem imItem = new IconMenuItem("Name", "icon.png");
  4. imItem.getName();
Это всё будет работать.

Разберём теперь это:
  1. MenuItem item = items[i];
  2. if (item instanceof IconMenuItem) {
  3.     g.drawImage(((IconMenuItem) item).getIcon(), width / 4, startY + itemHeight * i, Graphics.TOP | Graphics.RIGHT);
  4. } else if (item instanceof ColorMenuItem) {
  5.     g.setColor(((ColorMenuItem)item).getColor());
  6. } else {
  7.     // item - MenuItem
  8.     g.setColor(0xFF095E15);
  9. }
instanceof проверяет, объект какого класса лежит перед нами. Ведь мы положили разные объекты в один массив, поэтому чтобы правильно вызвать метод getColor или getIcon, нужно знать, что перед нами именно ColorMenuItem и IconMenuItem соответственно. Если пункт меню - объект класса IconMenuItem (item instanceof IconMenuItem), то мы можем безболезненно привести item к типу IconMenuItem и достучаться к методу getIcon, чтобы нарисовать картинку.

Вообще, использование instanceof очень тормозит выполнение программы, поэтому не следует им злоупотреблять, особенно в цикле или при отрисовке.
В следующей статье я покажу, как с помощью переопределения методов можно сделать красивый компактный быстрый код.

  OOP-Demo.zip
+5   6   1
2870