"Amigo, adakah anda suka ikan paus?"

"Paus? Tidak, tidak pernah mendengar tentang mereka."

"Ia seperti lembu, hanya lebih besar dan ia berenang. Kebetulan, ikan paus berasal dari lembu. Eh, atau sekurang-kurangnya mereka berkongsi nenek moyang yang sama. Tidak mengapa."

Polimorfisme dan overriding - 1

"Dengar. Saya ingin memberitahu anda tentang satu lagi alat OOP yang sangat berkuasa: polimorfisme . Ia mempunyai empat ciri."

1) Kaedah mengatasi.

Bayangkan anda telah menulis kelas "Lembu" untuk permainan. Ia mempunyai banyak pembolehubah ahli dan kaedah. Objek kelas ini boleh melakukan pelbagai perkara: berjalan, makan, tidur. Lembu juga membunyikan loceng apabila mereka berjalan. Katakan anda telah melaksanakan segala-galanya dalam kelas hingga ke butiran terkecil.

Polimorfisme dan overriding - 2

Kemudian tiba-tiba pelanggan berkata dia mahu mengeluarkan tahap baru permainan, di mana semua tindakan berlaku di laut, dan watak utama adalah ikan paus.

Anda mula mereka bentuk kelas Paus dan menyedari bahawa ia hanya berbeza sedikit daripada kelas Lembu. Kedua-dua kelas menggunakan logik yang hampir sama, dan anda memutuskan untuk menggunakan warisan.

Kelas Lembu sangat sesuai untuk menjadi kelas induk: ia sudah mempunyai semua pembolehubah dan kaedah yang diperlukan. Apa yang anda perlu lakukan ialah menambah keupayaan ikan paus untuk berenang. Tetapi ada masalah: ikan paus anda mempunyai kaki, tanduk dan loceng. Lagipun, kelas Lembu melaksanakan fungsi ini. Apa yang kau boleh buat?

Polimorfisme dan overriding - 3

Kaedah mengatasi datang untuk menyelamatkan. Jika kita mewarisi kaedah yang tidak melakukan apa yang kita perlukan dalam kelas baharu kita, kita boleh menggantikan kaedah itu dengan kaedah yang lain.

Polimorfisme dan overriding - 4

Bagaimana ini dilakukan? Dalam kelas keturunan kami, kami mengisytiharkan kaedah yang kami mahu ubah (dengan tandatangan kaedah yang sama seperti dalam kelas induk) . Kemudian kami menulis kod baru untuk kaedah tersebut. Itu sahaja. Seolah-olah kaedah lama kelas induk tidak wujud.

Begini cara ia berfungsi:

Kod Penerangan
class Cow
{
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a cow");
}
}class Whale extends Cow
{
public void printName()
{
System.out.println("I'm a whale");
}
}
Di sini kita mentakrifkan dua kelas:  Cow dan  WhaleWhalemewarisi  Cow.

Kelas  Whale mengatasi  printName();kaedah.

public static void main(String[] args)
{
Cow cow = new Cow();
cow.printName();
}
Kod ini memaparkan « Saya lembu » pada skrin.
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printName();
}
Kod ini memaparkan « Saya ikan paus » pada skrin

Selepas ia mewarisi Cowdan menimpa printName, Whalekelas sebenarnya mempunyai data dan kaedah berikut:

Kod Penerangan
class Whale
{
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a whale");
}
}
Kami tidak tahu apa-apa tentang kaedah lama.

"Sejujurnya, itulah yang saya jangkakan."

2) Tetapi bukan itu sahaja.

"Andaikan  Cow kelas mempunyai  printAllkaedah , yang memanggil dua kaedah lain. Kemudian kod itu akan berfungsi seperti ini:"

Skrin akan menunjukkan:
Saya putih
Saya ikan paus

Kod Penerangan
class Cow
{
public void printAll()
{
printColor();
printName();
}
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a cow");
}
}

class Whale extends Cow
{
public void printName()
{
System.out.println("I'm a whale");
}
}
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printAll();
}
Skrin akan menunjukkan:
Saya putih
Saya ikan paus

Ambil perhatian bahawa apabila kaedah printAll () kelas Lembu dipanggil pada objek Whale, kaedah printName() Whale akan digunakan, bukan Cow's.

Perkara yang penting bukanlah kelas kaedah ditulis, tetapi jenis (kelas) objek di mana kaedah dipanggil.

"Saya faham."

"Anda hanya boleh mewarisi dan mengatasi kaedah bukan statik. Kaedah statik tidak diwarisi dan oleh itu tidak boleh ditindih."

Inilah rupa kelas Paus selepas kami menggunakan pewarisan dan mengatasi kaedah:

Kod Penerangan
class Whale
{
public void printAll()
{
printColor();
printName();
}
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a whale");
}
}
Inilah rupa kelas Paus selepas kami menggunakan pewarisan dan mengatasi kaedah tersebut. Kami tidak tahu apa-apa tentang kaedah lama printName.

3) Jenis pemutus.

Berikut adalah perkara yang lebih menarik. Oleh kerana kelas mewarisi semua kaedah dan data kelas induknya, objek kelas ini boleh dirujuk oleh pembolehubah kelas induk (dan induk induk, dsb., terus ke kelas Objek). Pertimbangkan contoh ini:

Kod Penerangan
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printColor();
}
Skrin akan menunjukkan:
Saya putih.
public static void main(String[] args)
{
Cow cow = new Whale();
cow.printColor();
}
Skrin akan menunjukkan:
Saya putih.
public static void main(String[] args)
{
Object o = new Whale();
System.out.println(o.toString());
}
Skrin akan menunjukkan:
Whale@da435a.
Kaedah toString() diwarisi daripada kelas Objek.

"Barang bagus. Tapi kenapa awak perlukan ini?"

"Ia adalah ciri yang berharga. Anda akan faham kemudian bahawa ia sangat, sangat berharga."

4) Pengikatan lewat (penghantaran dinamik).

Inilah rupanya:

Kod Penerangan
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printName();
}
Skrin akan menunjukkan:
Saya ikan paus.
public static void main(String[] args)
{
Cow cow = new Whale();
cow.printName();
}
Skrin akan menunjukkan:
Saya ikan paus.

Ambil perhatian bahawa bukan jenis pembolehubah yang menentukan kaedah printName tertentu yang kami panggil (kelas Lembu atau Paus), sebaliknya jenis objek yang dirujuk oleh pembolehubah.

Pembolehubah Cow menyimpan rujukan kepada objek Whale , dan kaedah printName yang ditakrifkan dalam kelas Whale akan dipanggil.

"Nah, mereka tidak menambah itu demi kejelasan."

"Ya, ia tidak begitu jelas. Ingat peraturan penting ini:"

Set kaedah yang anda boleh panggil pada pembolehubah ditentukan oleh jenis pembolehubah. Tetapi kaedah/pelaksanaan tertentu yang dipanggil ditentukan oleh jenis/kelas objek yang dirujuk oleh pembolehubah.

"Saya akan cuba."

"Anda akan menghadapi perkara ini secara berterusan, jadi anda akan cepat memahaminya dan tidak pernah lupa."

5) Jenis tuangan.

Casting berfungsi secara berbeza untuk jenis rujukan, iaitu kelas, berbanding untuk jenis primitif. Walau bagaimanapun, penukaran melebar dan menyempitkan juga digunakan pada jenis rujukan. Pertimbangkan contoh ini:

Memperluaskan penukaran Penerangan
Cow cow = new Whale();

Penukaran pelebaran klasik. Kini anda hanya boleh memanggil kaedah yang ditakrifkan dalam kelas Lembu pada objek Paus.

Pengkompil akan membenarkan anda menggunakan pembolehubah lembu hanya untuk memanggil kaedah tersebut yang ditakrifkan oleh jenis Lembu.

Menyempitkan penukaran Penerangan
Cow cow = new Whale();
if (cow instanceof Whale)
{
Whale whale = (Whale) cow;
}
Penukaran penyempitan klasik dengan semakan jenis. Pembolehubah lembu jenis Lembu menyimpan rujukan kepada objek Paus.
Kami menyemak sama ada ini berlaku , dan kemudian melakukan penukaran jenis (pelebaran). Ini juga dipanggil pemutus jenis .
Cow cow = new Cow();
Whale whale = (Whale) cow; //exception
Anda juga boleh melakukan penukaran menyempitkan jenis rujukan tanpa menyemak jenis objek.
Dalam kes ini, jika pembolehubah lembu menunjuk pada sesuatu selain objek Whale, pengecualian (InvalidClassCastException) akan dilemparkan.

6) Dan sekarang untuk sesuatu yang lazat. Memanggil kaedah asal.

Kadangkala apabila mengatasi kaedah yang diwarisi, anda tidak mahu menggantikannya sepenuhnya. Kadang-kadang anda hanya mahu menambah sedikit padanya.

Dalam kes ini, anda benar-benar mahu kod kaedah baharu memanggil kaedah yang sama, tetapi pada kelas asas. Dan Java membolehkan anda melakukan ini. Begini caranya:  super.method().

Berikut adalah beberapa contoh:

Kod Penerangan
class Cow
{
public void printAll()
{
printColor();
printName();
}
public void printColor()
{
System.out.println("I'm white");
}
public void printName()
{
System.out.println("I'm a cow");
}
}

class Whale extends Cow
{
public void printName()
{
System.out.print("This is false: ");
super.printName();

System.out.println("I'm a whale");
}
}
public static void main(String[] args)
{
Whale whale = new Whale();
whale.printAll();
}
Skrin akan menunjukkan:
Saya putih
Ini palsu: Saya lembu
Saya ikan paus

"Hmm. Baiklah, itu satu pengajaran. Telinga robot saya hampir cair."

"Ya, ini bukan perkara yang mudah. ​​Ia adalah antara bahan yang paling sukar yang akan anda hadapi. Profesor berjanji untuk memberikan pautan kepada bahan daripada pengarang lain, supaya jika anda masih tidak memahami sesuatu, anda boleh mengisi jurang."