Полиморфизм в Java
Полиморфизм (греч. «много форм») — третий принцип ООП. В Java он проявляется в том, что ссылка одного типа (обычно — общего родительского) может указывать на объекты разных подклассов, и при вызове метода через эту ссылку выполнится та реализация, которая определена в фактическом классе объекта. Это называется динамической диспетчеризацией.
Полиморфизм позволяет писать обобщённый код: метод принимает параметр типа Device, а работает с любым его наследником — Led, Motor, Sensor — не зная о них заранее. Чтобы добавить новый вид устройства, не нужно менять старый код — достаточно создать новый класс-наследник и переопределить нужные методы.
Различают два вида полиморфизма:
Статический (compile-time) — перегрузка методов.
Динамический (runtime) — переопределение методов в подклассах, о котором эта статья.
Пример 1. Метод вызывается по фактическому типу
class Device {
void run() {
System.out.println("Device.run");
}
}
class Led extends Device {
@Override
void run() {
System.out.println("Led.run — blink");
}
}
public class Motor extends Device {
@Override
void run() {
System.out.println("Motor.run — rotate");
}
public static void main(String[] args) {
Device a = new Device();
Device b = new Led();
Device c = new Motor();
a.run();
b.run();
c.run();
}
}
Output:
Device.run
Led.run — blink
Motor.run — rotate
Пример 2. Массив разных устройств
class Device {
void info() {
System.out.println("[Device]");
}
}
class Sensor extends Device {
@Override
void info() { System.out.println("[Sensor]"); }
}
class Servo extends Device {
@Override
void info() { System.out.println("[Servo]"); }
}
public class Hub {
public static void main(String[] args) {
Device[] all = { new Device(), new Sensor(), new Servo() };
for (Device d : all) {
d.info();
}
}
}
Output:
[Device]
[Sensor]
[Servo]
Пример 3. Метод принимает базовый тип
class Device {
void start() {
System.out.println("device starts");
}
}
class Led extends Device {
@Override
void start() { System.out.println("led on"); }
}
class Motor extends Device {
@Override
void start() { System.out.println("motor spins"); }
}
public class Bus {
static void launch(Device d) {
d.start();
}
public static void main(String[] args) {
launch(new Led());
launch(new Motor());
launch(new Device());
}
}
Output:
led on
motor spins
device starts
Пример 4. instanceof и приведение типа
class Device {
String name = "?";
}
class Sensor extends Device {
int value = 42;
}
public class Motor extends Device {
int speed = 100;
public static void main(String[] args) {
Device[] all = { new Sensor(), new Motor() };
for (Device d : all) {
if (d instanceof Sensor) {
Sensor s = (Sensor) d;
System.out.println("Sensor val=" + s.value);
} else if (d instanceof Motor) {
Motor m = (Motor) d;
System.out.println("Motor speed=" + m.speed);
}
}
}
}
Output:
Sensor val=42
Motor speed=100
Пример 5. toString — переопределение метода Object
public class Led {
int pin;
boolean on;
public Led(int pin, boolean on) {
this.pin = pin;
this.on = on;
}
@Override
public String toString() {
return "Led{pin=" + pin + ", on=" + on + "}";
}
public static void main(String[] args) {
Led a = new Led(13, true);
Led b = new Led(9, false);
System.out.println(a);
System.out.println("b=" + b);
}
}
Output:
Led{pin=13, on=true}
b=Led{pin=9, on=false}
Пример 6. Поля не полиморфны
class Device {
String name = "Device";
}
public class Sensor extends Device {
String name = "Sensor";
public static void main(String[] args) {
Sensor s = new Sensor();
Device d = s;
System.out.println("s.name=" + s.name);
System.out.println("d.name=" + d.name); // обращение к полю — по типу ссылки
}
}
Output:
s.name=Sensor
d.name=Device
Подводные камни
Поля решаются статически по типу ссылки, а методы — динамически по фактическому классу объекта. Не полагайтесь на «скрытые» поля.
static-методы тоже не полиморфны: они привязаны к классу, а не к объекту.privateиfinalметоды переопределить нельзя — они также вызываются «по типу ссылки».Безопасное приведение типа делайте после
instanceof, иначе словитеClassCastException.Чрезмерные
instanceof + cast— признак того, что вы не используете полиморфизм по назначению. Часто лучше добавить нужный метод в базовый класс или интерфейс.
См. также
Примечание
Лицензия и источники
Концепция описана на основе официального Java Tutorial от Oracle (https://docs.oracle.com/javase/tutorial/), Oracle Free Documentation License. Перевод, примеры и пояснения — © AlashEd Wiki.