Не могу понять полиморфизм Java
Учил джаву и никак не могу понять, зачем использовать полиморфизм, для чего делать, пустой класс скажем человека, объявлять пустые методы, а после этого создавать одного, второго, третьего и т.д. человека, используя класс человека? Не проще ли сразу создать классы людей? Ведь чтобы их использовать в любом случае придётся к ним обратится, так в чём суть?
Смотрел разные примеры, видео, но этот вопрос меня никак не покидает, вот к примеру последнее видео, которое я смотрел — youtu.be/...uaF0s?si=MtKNlgzhXWW-MQCX
Здесь автор делает пример из доты, в котором делает с помощью полиморфизма из статической типизации — динамическую, ну как я понял.
А чем это поможет если по сути, класс Hero, имеет в себе классы всех персонажей, то есть по логике, когда с помощью него объявляется 5 переменных с героями, в каждой переменной нет конкретного героя, как понять кого надо вызвать именно? Как я понимаю уже в будущем коде надо это реализовать, но тогда каким образом понять какого героя надо выбрать? И в последующем, вызвать класс нужного героя, не лучше ли сразу определиться и вызвать нужные классы, вместо того чтобы вызывать все, а работать с определёнными? Или может я что-то не понимаю, в любом случае если у вас есть пример полиморфизма, в котором не возникнет вопрос, а нафига всё усложнять, то буду рад его прочитать, потому как что такое полиморфизм, и как он примерно работает я знаю, но не понимаю.
Ответы (2 шт):
Максим! Полиморфизм - множество форм. Первое, что пришло в голову, это пример с животными. Создадим интерфейс животное (Animal).
interface Animal {
String voice();
}
В нём только один метод, voice(), без определения поумолчанию. Так как животные могут быть разные, звуки они тоже издают разные и на данном уровне абстракции мы не знаем, конкретно какие животные будут реализовывать этот интерфейс. Потому нам не известно, какие звуки они будут издавать. Добавим классы каких-то животных, в моём случае их три: Cat, Dog, Mouse.
class Cat implements Animal {
@Override
public String voice() {
return "Мяу, мяу..."; // Кошка мяукает
}
}
class Dog implements Animal {
@Override
public String voice() {
return "Гав, гав..."; // Собака лает
}
}
class Mouse implements Animal {
@Override
public String voice() {
return "Пи, пи..."; // Мышь пищит
}
}
Теперь создадим класс Main с методом main(), откуда и будет стартовать наша программа.
public class Main {
public static void main(String[] args) {
Animal[] animals = {
new Dog(),
new Cat(),
new Mouse()
};
for (Animal animal : animals) {
System.out.println(animal.getClass().getSimpleName() + ": " + animal.voice());
}
}
}
В методе main() создаём массив типа Animal и добавляем в него наших животных, после чего в цикле проходимся по массиву и вы вводим их голоса в консоль. Обрати внимание, что массив у нас имеет тип Animal, т.е. тип интерфейса. Интерфейс как бы обязует каждый класс, который будет его реализовывать, создать свой метод voice(), который и будет возвращать нужный звук, в зависимости от того, какое именно животное его издаёт. Это и есть полиморфизм, точнее одно из его определений. Вообще в сети много ресурсов, где об этом можно почитать, более подробно. Если, что-то не понятно, уточняй, что именно.
Хм, давайте на примере, который вы как раз и привели. Герои из Доты. Есть, скажем, абстрактный класс Hero, который какую-то общую для всех героев логику может содержать, повышение уровня в зависимости от количества опыта, смерть при 0 хп, ну и так далее.
Однако большая часть его методов, как вы сказали - пустые. То есть методы объявлены, но не реализованы, у них нет тела. Для темы с героем они могут быть, к примеру, такие:
public abstract int calcHandDamage();
public abstract void dance();
public abstract void castFirstSkill();
public abstract void castSecondSkill();
public abstract void castThirdSkill();
public abstract void castFourthSkill();
И так далее. Вот у нас есть абстрактный герой Hero. Знать, как отыгрывать, будут его наследники. Они реализуют заявленные абстрактными методы, каждый по-своему. Но вот в чём фишка. С самыми разными героями, хоть убийцами, хоть магами, хоть стрелками, с разными пассивками и активными скиллами, можно будет взаимодействовать одним и тем же образом – через эти методы.
Вы спросили, почему бы сразу не создать классы этих героев, зачем нам какой-то пустой Hero? Дело вот в чём. Заранее мы можем не знать, какой из героев нам понадобится. Разве разработчики Доты знают, какого персонажа каждый игрок решит пикнуть в очередной катке? Поэтому, в зависимости от пика игрока, создаётся объект нужного героя. Но чтобы в ходе игры вызывать эти все методы, нам вовсе необязательно знать, какого именно персонажа выбрал игрок. Ведь у них всех единые методы. Единая суть во многообразии форм.
Таким образом, если игрок выбрал Пуджа:
Hero selectedHero = new Pudge(); // класс Pudge наследуется от Hero
Если игрок выбрал Инвокера:
Hero selectedHero = new Invoker(); // класс Invoker тоже наследуется от Hero
А уж если решил выбрать Анти Мага:
Hero selectedHero = new AntiMage(); // не поверите, но и класс AntiMage - наследник класса Hero
Затем вам просто нужно вызывать соответствующие методы объекта, хранящегося в selectedHero. Вызван будет метод именно того объекта, который вы поместили в переменную selectedHero. Хотя она имеет тип Hero, но хранится-то там объект другого класса - класса-наследника Hero. Так что если там Пудж, то будут вызваны методы, реализованные для Пуджа. И так далее.
selectedHero.dance();
selectedHero.castFirstSkill();
int damage = selectedHero.calcHandDamage();
Вы вообще не обязаны знать, какой именно герой там хранится. Ко всем ним одинаково можно обращаться. Не нужно прописывать кучу if-ов или ещё какой мути. Структура программы и её логика становятся чище, прозрачнее, красивее. Примерно в этом заключается суть полиморфизма.