воскресенье, 29 января 2012 г.

Объектрно-ориентированное программирование. Наследование


Давайте поговорим о наследовании (inheritance) в ООП и о тех возможностях, которые появляются при его использовании.

Наследование позволяет описать новый класс на основе уже созданного класса. Тем самым передавая все публичные методы и свойства базового класса дочернему классу.


Для чего оно нужно
Когда вы пишите код, то можете заметить, что некоторые классы содержат повторяющиеся по реализации методы и свойства. А теперь представьте, что у вас таких классов, не два, а намного больше. В один прекрасный день, вам пришла в голову гениальная идея, как упростить реализацию, метода который дублируется во многих классах. В таком случае вам придется проходиться по всем классам и проводить однотипные действия, по изменению кода. Еще не гарантия, что при обновлении кода, вы не пропустите, какой-нибудь класс. В итоге получите некорректно работающий код. И опять придется искать то место, которое некорректно работает. Куда намного проще вынести этот метод в отдельный класс, который, в дальнейшем будет наследоваться. Это уменьшает количество строк кода, и увеличивает гибкость кода.


Как это работает
Для того чтобы произвести наследование необходимо после названия класса, перед фигурной скобкой, которая открывает тело класса, прописать служебное слово extends и за ним название базового класса. При этом дочерний класс может унаследовать только один базовый класс, такова идеология java и с этим ничего не поделаешь. Пример:

package my.inheritance;

class BaseClass {
      
       private String msg = "Test message";
      
       public String getMessage() {
             returnmsg;
       }
      
       public void setMessage(String str) {
             msg += str;
       }
}

public class ChildrenClass extends BaseClass {
      
public static void main( String[] args )
    {
       ChildrenClass cc = new ChildrenClass();
      
cc.setMessage(" children class.");
System.out.println(cc.getMessage() );
}
}
/* результат:
* Test message children class
*/

Из данного примера видно, что класс ChildrenClass не имевший ни одного метода, кроме метода main, без проблем добавляет, конкатенирует (соединяет) и выводит строку. При этом нам ничего не мешает добавить свои методы в класс ChildrenClass.
Заметьте, что наследуются все методы, свойства базового класса, однако дочерний класс не может обращаться к элементам объявленным как private.


Переопределение метода
Так же в Java есть возможность переопределять методы базовых классов, при условии, что они не имеют модификатора final,который запрещает переопределение метода. Немного отступив от сути рассказа, хотелось бы заметить, что если модификатор final стоит перед названием класса, то данный класс не может наследоваться.
И так, в дочернем классе можно переопределять реализацию методов базового класса. Это позволяет писать довольно гибкий код. Пример:

package my.inheritance;

class BaseClass {
      
       private static final String MESSAGE = "Test message";
       private static String msg;
      
       public String getMessage() {
             return msg;
       }
      
       public void setMessage(String str) {
             msg = MESSAGE + str;
       }
      
       public void setNewMessage(String str) {
             setMessage(": "+ str);
       }
}

public class ChildrenClass extends BaseClass {
      
       // переопределение базового класса
public void setNewMessage(String str) {
            
             setMessage(" - " + str);
       }
      
    public static void main( String[] args ) {
       ChildrenClass cc = new ChildrenClass();
      
       cc.setMessage(" children class.");
        System.out.println( cc.getMessage() );
       
        // вызывается переопределенный метод базового класса
        cc.setNewMessage(" children new method.");
        System.out.println(cc.getMessage());
       
        // вызывается метод базового класса
        BaseClass bc = new BaseClass();
        bc.setNewMessage(" base new method.");
        System.out.println(cc.getMessage());
       
    }
}
/* результат:
* Test message children class.
* Test message – children new method.
* Test message: base new method.
*/

Как видно из данного примера метод setNewMessage() был переопределен. При этом вам ничего не мешает вызвать метод базового класса.


Инициализация классов
При наследовании сначала происходит инициализация базового класса, а уж потом дочернего. Если в иерархии наследования участвует более двух классов, то инициализация начинается от самого  базового  класса, к самому дочернему. Java автоматически вставляет вызов конструктора базового класса в конструктор дочернего класса. Давайте рассмотрим пример:

package my.inheritance;

class One {
      
       One() {
             System.out.println("Construct One.");
       }
}

class Two extends One {
      
       Two() {
             System.out.println("Construct Two.");
       }
}


public class Three extends Two {
      
       public Three() {
             System.out.println("Construct Three");
       }
    public static void main( String[] args ) {
       Three three = new Three();
       
    }
}
/* результат:
* Construct One
* Construct Two
* Construct Three
*/

Как видно из примера первым был вызван конструктор класса One, а потом все его дочерние классы.
В случае если конструктор базового класса имеет аргументы, реализация немного меняется. Тут вам самим придется следить за передачей аргументов базовому классу, с указанием ключевого слова super(), и передачей аргументов. Ключевое слово super() должно быть первой командой в конструкторе дочернего класса.
Это вызвано тем, что Java автоматически, конструкторы с аргументами, не создает. Если вы не вызовите конструктор базового класса, с передачей ему аргументов, компилятор вернет ошибку.
Передача аргументов базовому классу приведена в примере ниже:

package my.inheritance;

class One {
      
       One( String str ) {
             System.out.println("Construct One. Argument: " + str);
       }
}

class Two extends One {
      
       Two( String str ) {
             super( str );
             System.out.println("Construct Two. Argument: " + str);
       }
}


public class Three extends Two {
      
       public Three( String str ) {
             super( str );
             System.out.println("Construct Three. Argument: " + str);
       }
    public static void main( String[] args ) {
       Three three = new Three("running");
       
    }
}

Как видите, в конструкторах классов Two и Three первой командой идет вызов ключового слова super, с аргументом str.

Комментариев нет:

Отправить комментарий