Освоюємо Kotlin/Поліморфізм
Поліморфізм (з грецької "багато форм") в ООП — це можливість використання змінних батьківських типів замість типів похідних класів. При цьому можна викликати різні реалізації однієї і тієї ж функції похідних класів, якщо така функція передбачена в базовому класі або ж інтерфейсі. Тобто можна схожим чином поводитися з об'єктами різних класів використовуючи змінну батьківського типу. Поліморфізм один з основних принципів ООП поряд з абстрагуванням, інкапсуляцією та успадкуванням.
Person, Doctor та Patient
[ред.]Приклад, маємо інтерфейс Person, що передбачає функцію introduce() (відповідає за представлення себе). Також маємо два похідні класи Doctor та клас Patient, кожен з яких має власну реалізацію функції introduce(). Person може не обов'язково бути інтерфейсом, це може бути абстрактний клас або ж звичайний клас.
interface Person{
fun introduce(){
println("Я особа")
}
}
class Doctor(var name:String, var specialization:String):Person{
override fun introduce() {
println ("Я $name, моя спеціалізація $specialization")
}
fun getSpecialization(){
println (specialization)
}
}
class Patient(var name:String, var illness:String):Person{
override fun introduce() {
println ("Я $name, я хворію на $illness")
}
fun getIllness(){
println (illness)
}
}
Поліморфізм в дії
[ред.]Створимо чотири особи (двох хворих і двох лікарів) і заставимо їх представитись. Для представлення створимо масив осіб типу Person і в циклі переберемо кожну особу і викличемо функцію introduce():
fun main(){
val person1:Person = Doctor ("Іван Сидоренко", "хірург")
val person2:Person = Patient ("Володимир Митко", "грип")
val person3:Person = Patient ("Микита Ротко", "ангіна")
val person4:Person = Doctor ("Таня Никитенко", "терапевт")
val hospital=arrayOf<Person>(person1, person2, person3, person4) //лікарня у вигляді масиву осіб
for (person in hospital){
person.introduce() // кожна особа представиться по свому в залежності це лікар чи хворий
}
}
Результат:
Я Іван Сидоренко, моя спеціалізація хірург Я Володимир Митко, я хворію на грип Я Микита Ротко, я хворію на ангіна Я Таня Никитенко, моя спеціалізація терапевт |
Як бачимо використовуючи інтерфейсну змінну типу Person ми заставили кожну особу представитись по свому. При цьому викликалась реалізація функції introduce() або класу Doctor або класу Patient.
Можна створити функцію, яка прийматиме на вхід змінну типу Person як аргумент і також виводитиме інформацію про особу:
fun personIntroducing(person:Person){
person.introduce()
}
fun main(){
val person1:Person = Doctor ("Іван Сидоренко", "хірург")
val person2:Person = Patient ("Володимир Митко", "грип")
val person3:Person = Patient ("Микита Ротко", "ангіна")
val person4:Person = Doctor ("Таня Никитенко", "терапевт")
personIntroducing(person1)
personIntroducing(person2)
personIntroducing(person3)
personIntroducing(person4)
}
Результат виконання буде такий же, як і при використанні масиву.
Приведення до типу похідного класу
[ред.]Який мінус використання змінної батьківського типу? Основний недолік - це неможливість виклику власних функцій похідних класів, які не передбачені спільним інтерфейсом. Тобто ми не можемо написати person1.getSpecialization(). Щоб це зробити ми маємо привести змінну до типу похідного класу функцію, якого ми хочемо викликати. Так:
val person1:Person = Doctor ("Іван Сидоренко", "хірург")
val doctor=person1 as Doctor // привести змінну до типу Doctor
doctor.getSpecialization() // тепер можемо викликати
Зворотнє проведення змінних із типу Doctor в тип Person також можливе.
При такому приведенні може трапитися аварійна ситуація (виняток) і програма завершить виконання, якщо в змінній person1 виявиться не представник типу Doctor, а представник типу Patient. Тому попередньо необхідно зробити перевірку на тип. Для цього використовується умова і ключове слово is:
if (person1 is Doctor){ // перевіряємо чи особа є лікарем
val doctor=person1 as Doctor //приводимо особу до типу Doctor
doctor.getSpecialization() // тепер можемо викликати функцію getSpecialization класу Doctor
}