Skip to content
Aleksandr Kuchuk edited this page Aug 3, 2016 · 1 revision

##Введение Java у нас все-таки ООП ЯП, а значит какое-никакое введение в это самое ООП надо бы написать. Опять же - напишу только то, что думаю или читал сам. В тонкости влезать совсем уж не будем, ибо холиварная тема.

ООП

Прежде всего, какие принципы ООП мы знаем?В основном, это:

  • Инкапсуляция
  • Наследование
  • Полиморфизм

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

Наследование

Наследование(is a)

Прежде всего, наследование - это значит, что наш класс-наследник - это класс, который является тем же, что и родитель, т.е это - is a. У него те же свойства и какие-то параметры, он только расширяет родителя, но никак не сужает его возможности. Если наш класс не удовлетворяет принципу is a - то применять здесь наследование - ошибочно. Т.е если у нас есть класс User и класс Adminm или там класс Figure и класс Rectangle, то тут применение наследования - верный путь, true.

Плюсы наследования:

  • Повторное использование уже существующих и протестированных участков кода
  • Выстраиваемая иерархия наследников

Минусы наследования:

  • Дочерний класс зависит от изменений в родительском классе, изменив что-то в родительском - мы автоматически получаем эти изменения в дочернем. Пусть у нас есть своя какая-то MyHashMap и мы отнаследовали ее от HashMap, переопределили метод add(...), если разработчики HashMap введут addAll(..) - у нас будет в этом месте дыра, ведь мы переопределили уже add по-своему, но не addAll, который будет добавлять элементы 'по старому'.
  • Ошибка в неверной иерархии наследования - и у нас огромные проблемы с расширением нашего кода в дальнейшем.

Использовать наследование надо крайне аккуратно, все время проверяя на is a правило.

Также стоит отметить, что при наследовании и создании дочернего класса - вызывается конструктор родительского!

Другой подход - это композиция

Композиция(has a)

Композиция - это когда класс обладает свойствами своих составных частей. Т.е мы составляем наш класс из каких-то кирпичиков. Один из способов как в Java реализовать множественное наследование. Т.е если у нас есть, например, класс HDD и класс Notebook, то Notebook включает в себя HDD, т.е удовлетворяет has a принципу. Т.е это расширение функционала класса за счет 'внедрения' других классов.

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

Минусы такого подхода только в том, что иногда действительно удобнее работать с чистым наследованием и иерархией классов.

Наследование - это именно 'расширение' какого-то функционала, а композиция - включение(внедрение). При наследовании мы всегда обязаны помнить про родителя, про его изменения и его поведение, поэтому часто композиция - более лучший выбор.

Инкапсуляция

Это контроль доступа к полям и методам класса, скрываем реализацию от внешних глаз. Мы скрываем наши данные, чтобы их не мог кто угодно менять. И даже если могли менять - чтобы мы могли контролировать это на ошибки, т.е если у нас есть класс Person и там поле age, то мы не можем поставить отрицательный возраст. Также мы можем реализацию какую-то изменить и ничего не испортить.

Т.е, если суммировать:

  • Контроль доступа
  • Контроль валидности данных
  • Возможность изменения реализации

Некоторые подробности тут: More about

Полиморфизм

Это возможность работать с несколькими типами так, как-будто это один и тот же тип, при этом поведение каждого типа будет зависеть от реализации.

  1. Дочерний класс может быть использован везде, где используется родительский класс.
  2. Если дочерний класс приведен к родительскому, то доступны методы и переменные только родительского.
  3. Реализация вызывается из дочернего класса(которая @Override)

Подробнее о каждом:

  1. Т.е если у нас есть User и Admin, который тоже User, то мы можем везде передать и User, и Admin.
  2. При касте к родительскому классу мы как бы поднимаемся по иерархии наследования вверх, поэтому работаем уже по сути с родителем.
  3. Реализация методов вызывается же из дочернего, т.е если у нас есть Admin, который переопределил какие-то методы, то мы все равно вызовем эти методы, даже если приведем к User-у.

Чтобы легче понять представьте такую картинку(или нарисуйте): слева ссылки, справа объекты.

Пусть у нас есть ссылка типа User a и ссылка типа Admin b, обе ссылки указывают на объект Admin. Если вызовем a.method() и b.method() - вызовется всегда method() нашего объекта, т.е Admin. Т.е ссылки лишь показывают функционал, предоставляют api, а реализация уже вызывается из объекта.

//todo дописать про полиморфизм

Связывание

Это относится к полиморфизму в Java. Итак, бывает:

  • Раннее связывание
  • Позднее связывание
Раннее связывание

Это Overloading, происходит в compile time - это поиск подходящей сигнатуры в зависимости от параметров.

Простыми словами - это когда у нас есть несколько методов с одним именем, например, sum(), один принимает два аргумента, другой три, третий коллекцию.

Позднее связывание

Это Overriding, происходит уже в runtime - это поиск подходящей реализации по реальному типу объекта.

Это уже во время исполнения приложения jvm умеет искать подходящую реализацию, дочернего или родительского класса и вызывает ее. Именно поэтому пример в разделе полиморфизма с разными типами ссылок на один и тот же объект давали такой результат.

Пример:

public class PExample {
    public static void main(String[] args) {
        User user = new Admin();
        use(user);
    }

    public static void use(User user) {
        user.test();
    }
}

class User {
    public void test() {
        System.out.println("User invoke test");
    }
}

class Admin extends User {
    @Override
    public void test() {
        System.out.println("Admin invoke test");
    }
}

Вызовется именно метод Admin-а, и выведет на консоль: Admin invoke test.

Clone this wiki locally