Kodomo

Пользователь

Учебная страница курса биоинформатики,
год поступления 2010

ООП в Java: Наследование, абстрактные классы и методы.

Итак, мы разобрали, что класс – это, по сути, новый тип данных. Класс является шаблоном, по которому потом создаются объекты этого класса. В классе мы задаем переменные, которые должны быть у каждого объекта данного класса. Также в классе мы определяем методы, которые задают функциональность объектов данного класса, то есть то, что должный уметь делать объекты данного класса (что-то вроде должностной инструкции). Часто при написании программ возникает необходимость сделать новый класс, который чем-то отличается от уже имеющегося. Например, SmallBoss.

   1 Class SmallBoss{
   2 String name;
   3 
   4 Boolean isSleeping(){…}
   5 Boolean playComputerGames(){…}
   6 }

В этом случае нам придется копировать весь код из класса Boss и дописывать новый метод playComputerGames. Это приводит к размножению одинакового кода. Причем, если в последствии Вы найдете ошибку в этом коде, ее надо будет исправить в нескольких местах. Это неудобно. Поэтому, в Java есть механизм наследования. Мы можем сделать класс SmallBoss наследником класса Boss:

   1 Class SmallBoss extends Boss{
   2 Boolean playComputerGames(){…}
   3 }

Все объекты нового класса SmallBoss имеют те же переменные и методы, что и объекты класса Boss, плюс еще свой собственный метод playComputerGames(). Еще говорят, что класс SmallBoss расширяет класс Boss. Можно определить лишь один суперкласс для класса, который вы создаете. В нашей задаче про офис можно заметить, что классы Boss и Secretary содержат некоторое количество одинаковых полей и методов. Можно оптимизировать этот код при помощи наследования. Зададим класс Human и вынесем в него эти переменные и методы, а затем отнаследуем классы Boss и Secretary от класса Human.

   1 public class Human {
   2         
   3         String name;
   4         String surname;
   5 
   6         Human(String name, String surname)
   7         {
   8                 this.name = name;
   9                 this.surname = surname;
  10         }
  11         String getFullName()
  12         {
  13                 return name+" "+surname;
  14         }
  15         void speak(String phrase)
  16         {
  17                 System.out.println(getFullName()+":\t-"+phrase);
  18         }
  19 
  20 }

Теперь отнаследовав класс , например, Secretary от Human, мы автоматически имеем в этом классе переменные name и surname и методы getFullName() speak().

   1 public class Secretary extends Human
   2 {
   3         int max_nails_length = 35;
   4         int nails_length;
   5         boolean is_discharged = false;
   6         Printer printer;
   7 
   8 }

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

   1 Secretary(String name,String surname,int nails_length)
   2         {
   3                 //calling the constructor of superclass
   4                 super(name, surname);   
   5 
   6                 this.nails_length = nails_length;
   7                 
   8         }

Теперь, при создании объекта типа Secretary вначале вызывается конструктор супер-класса и заполняются переменные name и surname, а затем присваивается значение переменной nails_length. Ключевое слово super. Ключевое слово super используется для обращения не только к конструкторам суперкласса, но и к его методам. Чем-то похоже на this – только this обращается к методам данного класса, а super – родительского. Это используется, когда какие-то имена методов подкласса и суперкласса совпадают. А зачем это нужно? В случае наследования методы суперкласса уже определены, однако иногда наследник должен немного их изменить. например у нас Human имеет метод getFullName однако у босса мы бы хотели чтобы он добавлял к имени слово mr. а у секретарши miss, тогда мы можем переопределить (или еще говорят перегрузить) эти методы в наследнике:

   1 String getFullName() {
   2         return "mr. "+super.getFullName();
   3 }

При этом для получения собственно имени мы вызываем метод суперкласса. Еще одна особенность. Переменная суперкласса может ссылаться на объект подкласса, например,

   1 Human s = new Secretary("Mary", "Bork", 20);
   2 s.speak();
   3 s.getFullName();

Но у переменной типа Human нельзя вызвать метод, который определен в Secretary, но не определен в Human:

   1 s.getNailsLength();   //--  нельзя, 
   2                       //надо так:
   3 ((Secretary)s).getNailsLength();

То есть, то, к каким элементам можно обращаться, определяется типом ссылки, а не типом объекта, на который она ссылается. А вот, какой код будет выполняться при вызове метода (если он перегружен) — зависит уже от типа объекта. Зачем это надо? Например, вы хотите создать массив и положить в него и боса и секретарш (например, список в отделе кадров). Тогда вы сделаете массив типа Human и смело можете класть туда объекты классов, наследующих от Human.

Маленькая задачка на понимание полиморфизма (перегрузки методов).

   1 Boss b = new Boss("Alfred", "Lee");
   2 System.out.println(b.getFullName());

что напечатает программа? А так:

   1 Human h = new Boss("Alfred", "Lee");
   2 System.out.println(h.getFullName());

А так:

   1 h = new Secretary("Mary", "Bork", 20);
   2 System.out.println(h.getFullName());

Явление, что классы с одинаковой спецификацией могут иметь различную реализацию, называется Полиморфизмом. Кратко смысл полиморфизма можно выразить фразой: Один интерфейс, множество реализаций.

Абстрактные классы и методы

В java суперклассы можно делать также абстрактыми. Делается с помощью слова abstract. Абстрактный класс служит для создания иерархии, объединения кода некоторой группы классов, которые будут от него наследовать. Невозможно создать объекты такого класса – он служит лишь как абстракция. Такие объекты были бы бесполезны, поскольку класс определен не полностью. В абстрактном классе можно определять абстрактные методы – методы без реализации. Реализованы они будут в классах-наследниках (подкласс должен обязательно переопределить их).

   1 public abstract class Figure {
   2         public int x;
   3         public int y;
   4         public int moveTo(int x, int y){
   5            this.x = x;
   6            this.y = y;
   7         }
   8         public abstract double getArea();
   9         public abstract void print();
  10 }
  11 public class Square extends Figure {
  12         public double double side;
  13         public Square(int x, int y, double side) {
  14            this.x = x;
  15            this.y = y;  
  16            this.side=side;
  17         }
  18         public double getArea() {
  19                 return side*side;
  20         }
  21 }
  22 public class Circle extends Figure {
  23         public double radius;
  24         public Circle(int x, int y, double radius) { 
  25            this.x = x;
  26            this.y = y; 
  27            this.radius=radius; 
  28         }
  29         public double getArea() {
  30                 return Math.PI*radius*radius;
  31         }
  32 }