Полиморфизм в Java
Храните разные объекты в переменной родительского типа, чтобы одно имя вызова могло выполнять разные тела. У Machine держите horn, который печатает tone. У Truck перепишите horn, чтобы печатать honk. У Car перепишите horn, чтобы печатать beep. В Yard.java присвойте Machine m = new Machine();, Machine t = new Truck(); и Machine c = new Car();, затем вызовите horn у каждой. Скомпилируйте через javac Yard.java и запустите java Yard. На экране должны быть tone, затем honk, затем beep. Вторая демо хранит только двух потомков как ссылки Machine. equals оставьте для следующей страницы — этот урок про один родительский тип, который держит много форм.
Один родительский тип, три тела horn
Страница abstract оставила метод незавершённым, чтобы потомки его заполнили. Эта страница сосредоточена на вызове через переменную родительского типа. Откройте Yard.java. Держите имя public-класса Yard. Удалите оставшиеся только-abstract демо, если они хоронят историю с родительским типом, оставшиеся файлы enum Level, демо путей и любой клавиатурный Scanner. Программа не должна ждать ввода. Строка import не нужна.
Объявите обычного родителя с телом horn:
class Machine {
void horn() {
System.out.println("tone");
}
}
Объявите потомков через extends Machine с собственными телами horn, которые печатают honk и beep. Совпадающее имя и пустой список параметров означают, что каждый потомок переопределяет метод родителя.
Внутри Yard.main создайте три переменные, чей объявленный тип — Machine:
Machine m = new Machine();
Machine t = new Truck();
Machine c = new Car();
Затем вызовите:
m.horn();
t.horn();
c.horn();
Объявленный тип для всех трёх — Machine. Реальный объект решает, какое тело horn выполняется.
class Machine {
void horn() {
System.out.println("tone");
}
}
class Truck extends Machine {
void horn() {
System.out.println("honk");
}
}
class Car extends Machine {
void horn() {
System.out.println("beep");
}
}
public class Yard {
public static void main(String[] args) {
Machine m = new Machine();
Machine t = new Truck();
Machine c = new Car();
m.horn();
t.horn();
c.horn();
}
}
Из папки, где лежит Yard.java, запустите javac Yard.java. Когда скобки, фигурные скобки и точки с запятой совпадают, javac не пишет собственной строки. Затем запустите java Yard — не java Yard.java. Уберите окончание .java из команды запуска. Держите один public-класс в файле, чтобы имя совпадало с Yard. Вспомогательные типы в этой начинающей форме остаются без public.
Экран показывает tone, затем honk, затем beep. Пройдите запуск один раз. Java начинает с Yard.main. m хранит обычный Machine, поэтому m.horn() печатает tone. t хранит Truck, хотя тип переменной говорит Machine, поэтому t.horn() печатает honk. c хранит Car, поэтому c.horn() печатает beep. Одинаковое написание вызова. Три тела.
Указывайте на каждое присваивание, читая строку экрана, которая ему соответствует. Первая строка принадлежит обычной машине. Вторая строка принадлежит объекту truck за именем машинного типа. Третья строка принадлежит объекту car за именем машинного типа. Эта игра сопоставления — весь урок за один проход.
Если вы всё ещё видите только honk, затем park из abstract-демо, переключитесь на учебный файл polymorphism и снова запустите java Yard. Если каждая переменная типизирована только как Truck или Car, вы ещё не показываете родительскую форму этой страницы — держите тип Machine слева от присваиваний в демо.
Произнесите правило вслух, глядя на экран: тип переменной может быть родителем, объект может быть потомком, вызов выполняет тело потомка, когда потомок переопределил метод. Не изобретайте override equals на этой странице. Эта тема ждёт следующего урока.
Два потомка через ссылки Machine
Вторая программа сохраняет переопределения. main создаёт только Truck и Car, всё ещё хранит их как Machine, и вызывает horn у каждого.
class Machine {
void horn() {
System.out.println("tone");
}
}
class Truck extends Machine {
void horn() {
System.out.println("honk");
}
}
class Car extends Machine {
void horn() {
System.out.println("beep");
}
}
public class Yard {
public static void main(String[] args) {
Machine t = new Truck();
Machine c = new Car();
t.horn();
c.horn();
}
}
Скомпилируйте через javac Yard.java, затем запустите java Yard. Экран показывает honk, затем beep. В этой второй демо нет обычного объекта Machine, но обе переменные всё ещё типизированы как Machine. Объекты-потомки дают тела. Это та же идея polymorphism с более коротким экраном.
Прочитайте строки присваивания ещё раз: слева Machine, справа new Truck() или new Car(). Левая сторона называет родительский тип, через который вы вызываете. Правая сторона называет реальный объект. Держите extends Machine у обоих потомков, чтобы эти присваивания оставались допустимыми.
Если рядом ещё лежат abstract-демо, оставьте их как историю. Для этого урока запускайте учебный файл polymorphism, чтобы экран показал tone / honk / beep или honk / beep, а не park как главную историю. Если javac жалуется на дублирующие public-классы, оставьте public только у Yard.
Какое тело horn выполняется
📊 Who runs for each Machine variable
| Variable | Real object | horn prints |
|---|---|---|
m |
Machine |
tone |
t |
Truck |
honk |
c |
Car |
beep |
Сравнение с заполнением abstract
На странице abstract Machine оставил horn незавершённым, а потомки заполнили его, часто с общим park. На этой странице у Machine уже есть тело tone, потомки его переопределяют, а переменные родительского типа хранят объекты. Вы по-прежнему компилируете Yard.java и запускаете java Yard. Новые токены — присваивания родительского типа вроде Machine t = new Truck(); и три разных печати из одного имени вызова.
Если ещё открыт оставшийся abstract-файл, вы можете увидеть abstract void horn(); и park. Переключитесь на файл polymorphism, чтобы у Machine было обычное тело horn, а main присваивал переменные Machine разным объектам. Держите демо equals вне этой страницы. Держите клавиатурный ввод вне этой страницы.
Перед equals
Вы можете присваивать переменным Machine объекты Machine, Truck и Car и видеть tone, затем honk, затем beep. Вы можете хранить только двух потомков как ссылки Machine и видеть honk, затем beep. Компилируйте Yard.java и запускайте java Yard для каждой сохранённой демо по очереди. Вызов использовал владельца тела. Следующая страница — equals, где два объекта сравниваются на одинаковость безопасным для обучения способом.
Держите демо с тремя объектами для ясного экрана родителя плюс потомков. Держите демо с двумя потомками для ясной пары родительского типа. Держите файлы equals вне этой страницы. Эти три привычки — контрольная точка перед движением дальше. Когда honk появляется из truck-переменной типа Machine, polymorphism сделал работу этого урока, и вы готовы к следующей теме про equals.
Последний проход по папке: один public Yard, тела horn у родителя и потомков, переменные родительского типа в main, без ожидания клавиатуры, без принудительного equals. Запустите javac Yard.java и java Yard ещё раз на демо, которую держите открытой. Если экран всё ещё совпадает с таблицей, вызов многих форм крепок, и курс может перейти к equals на следующей странице.
🧠 Проверьте Свои Знания
Проверьте Свои Знания
Бросьте себе вызов с помощью этой интерактивной викторины и узнайте, насколько хорошо вы понимаете тему
📝 Инструкции
- Внимательно прочитайте каждый вопрос
- Выберите лучший ответ на каждый вопрос
- Вы можете пересдавать тест столько раз, сколько захотите
- Ваш прогресс будет показан вверху