-
Notifications
You must be signed in to change notification settings - Fork 3
##Введение Java у нас все-таки ООП ЯП, а значит какое-никакое введение в это самое ООП надо бы написать. Опять же - напишу только то, что думаю или читал сам. В тонкости влезать совсем уж не будем, ибо холиварная тема.
Прежде всего, какие принципы ООП мы знаем?В основном, это:
- Инкапсуляция
- Наследование
- Полиморфизм
Некоторые добавляют еще и абтсракицю туда, как четвертый. А есть и те, кто еще добавляет другие принципы. Но самые главные - это вот эти три. О них и поговорим.
Прежде всего, наследование - это значит, что наш класс-наследник - это класс, который является тем же, что и родитель, т.е это - is a
. У него те же свойства и какие-то параметры, он только расширяет родителя, но никак не сужает его возможности.
Если наш класс не удовлетворяет принципу is a
- то применять здесь наследование - ошибочно.
Т.е если у нас есть класс User и класс Adminm или там класс Figure и класс Rectangle, то тут применение наследования - верный путь, true.
Плюсы наследования:
- Повторное использование уже существующих и протестированных участков кода
- Выстраиваемая иерархия наследников
Минусы наследования:
- Дочерний класс зависит от изменений в родительском классе, изменив что-то в родительском - мы автоматически получаем эти изменения в дочернем.
Пусть у нас есть своя какая-то MyHashMap и мы отнаследовали ее от HashMap, переопределили метод
add(...)
, если разработчики HashMap введутaddAll(..)
- у нас будет в этом месте дыра, ведь мы переопределили уже add по-своему, но не addAll, который будет добавлять элементы 'по старому'. - Ошибка в неверной иерархии наследования - и у нас огромные проблемы с расширением нашего кода в дальнейшем.
Использовать наследование надо крайне аккуратно, все время проверяя на is a
правило.
Также стоит отметить, что при наследовании и создании дочернего класса - вызывается конструктор родительского!
Другой подход - это композиция
Композиция - это когда класс обладает свойствами своих составных частей. Т.е мы составляем наш класс из каких-то кирпичиков. Один из способов как в Java реализовать множественное наследование.
Т.е если у нас есть, например, класс HDD и класс Notebook, то Notebook включает в себя HDD, т.е удовлетворяет has a
принципу. Т.е это расширение функционала класса за счет 'внедрения' других классов.
В целом понятно, что мы не тащим за собой все из родительского объекта, а работаем с составными частями. Да еще и не вызываем конструкторы родительского класса при создании объекта.
Минусы такого подхода только в том, что иногда действительно удобнее работать с чистым наследованием и иерархией классов.
Наследование - это именно 'расширение' какого-то функционала, а композиция - включение(внедрение). При наследовании мы всегда обязаны помнить про родителя, про его изменения и его поведение, поэтому часто композиция - более лучший выбор.
Это контроль доступа к полям и методам класса, скрываем реализацию от внешних глаз. Мы скрываем наши данные, чтобы их не мог кто угодно менять. И даже если могли менять - чтобы мы могли контролировать это на ошибки, т.е если у нас есть класс Person и там поле age, то мы не можем поставить отрицательный возраст. Также мы можем реализацию какую-то изменить и ничего не испортить.
Т.е, если суммировать:
- Контроль доступа
- Контроль валидности данных
- Возможность изменения реализации
Некоторые подробности тут: More about
Это возможность работать с несколькими типами так, как-будто это один и тот же тип, при этом поведение каждого типа будет зависеть от реализации.
- Дочерний класс может быть использован везде, где используется родительский класс.
- Если дочерний класс приведен к родительскому, то доступны методы и переменные только родительского.
- Реализация вызывается из дочернего класса(которая @Override)
Подробнее о каждом:
- Т.е если у нас есть User и Admin, который тоже User, то мы можем везде передать и User, и Admin.
- При касте к родительскому классу мы как бы поднимаемся по иерархии наследования вверх, поэтому работаем уже по сути с родителем.
- Реализация методов вызывается же из дочернего, т.е если у нас есть 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.