создание объектов и переменных

в чем смысл создавать List list = new ArrayList(), а не ArrayList list = new ArrayList()? в чем смысл создавать, например, Animal dog = new Dog()?

много раз задавал эти вопросы ИИ и друзьям, которые уже имеют опыт в программировании, но никак не доходит


Ответы (1 шт):

Автор решения: leb

Начал с небольшого комментария, но ощущение недосказанности заставило расширить ответ. Ниже представлена идея, которая, ИМХО, должна помочь справиться с пониманием некоторых важных явлений и связанных с ними приёмов ООП.


В Java (да и в других компилируемых языках программирования, поддерживающих ООП) стоит отличать объект от переменной:

  • Объект -- реальная структура данных, расположенная в памяти.
  • Переменная -- манипулируемая ссылка на объект в памяти.

Так в выражении рода

<TYPE> some_var = new <TYPE>();

, запись слева от знака = ответственна за объявление переменной [<TYPE> some_var], в то время как запись справа от знака -- за инициализацию объекта [new <TYPE>()]. Типы в Java, как известно, статичны. Переменные используются для доступа к объектам в памяти.

Если ты создаёшь переменную, указывающую на объект ArrayList

<TYPE> some_var = new ArrayList();

, то логично было бы дать ей тип соответсвенно объекту:

ArrayList some_var = new ArrayList();

Но в Java переменная может указывать на объект любого типа, наследующего или реализующего её тип. Иначе говоря, если тип B является потомком типа A, то на этот объект типа B может указывать переменная как типа B, так и типа A.

Отсюда мы узнаём, что класс нашего объекта ArrayList наследует от класса AbstractList, который в свою очередь наследует от класса AbstractCollection. Это значит, что мы вполне вольны писать так:

//                                             List
//                                              ^
//                                              |
// AbstractCollection  <-  AbstractList  <-  ArrayList

ArrayList some_var_0 = new ArrayList();
AbstractList some_var_1 = new ArrayList();
AbstractCollection some_var_2 = new ArrayList();
// или, как в нашем случае, обратиться к интерфейсу, который реализует класс нашего объекта:
List some_var_3 = new ArrayList();

Использование таких обобщённых типов может быть полезно, если тип реального объекта заранее неизвестен, например:

List some_var;


switch ( mystic_var ) {
    case 0:  some_var = new ArrayList(); break;    // класс ArrayList реализует интерфейс List
    case 1:  some_var = new LinkedList(); break;   // класс LinkedList тоже реализует интерфейс List
    default: some_var = new Vector(); break;       // класс Vector тоже реализует интерфейс List
}

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

Полиморфизм достаточно распространён в языках программирования, поддерживающих ООП. Даже если в этом нет необходимости в ваших нынешних задачах, реализация полиморфизма не повлияет негативно на работу программы, но предоставит при этом больше гибкости и простора для будущего расширения и перестраивания программы. Хотя, опять же, никакой необходимости в нём может и не быть (тем более, если его польза пока не явна).

Итого

Использование обобщённых типов необязательно. Оно является, скорее, житейской мудростью или лайфхаком.

→ Ссылка