создание объектов и переменных
в чем смысл создавать List list = new ArrayList()
, а не ArrayList list = new ArrayList()
? в чем смысл создавать, например, Animal dog = new Dog()
?
много раз задавал эти вопросы ИИ и друзьям, которые уже имеют опыт в программировании, но никак не доходит
Ответы (1 шт):
Начал с небольшого комментария, но ощущение недосказанности заставило расширить ответ. Ниже представлена идея, которая, ИМХО, должна помочь справиться с пониманием некоторых важных явлений и связанных с ними приёмов ООП.
В 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
}
Приведённый выше код является типичным примером полиморфизма, при котором переменная может указывать на объекты различных типов. Именно из-за возможностей полиморфизма использование переменной более обобщённого типа зачастую оказывается более предпочтительным, чем использование конкретного соответствующего объекту.
Полиморфизм достаточно распространён в языках программирования, поддерживающих ООП. Даже если в этом нет необходимости в ваших нынешних задачах, реализация полиморфизма не повлияет негативно на работу программы, но предоставит при этом больше гибкости и простора для будущего расширения и перестраивания программы. Хотя, опять же, никакой необходимости в нём может и не быть (тем более, если его польза пока не явна).
Итого
Использование обобщённых типов необязательно. Оно является, скорее, житейской мудростью или лайфхаком.