Pewarisan Berbilang di Jawa
Warisan berbilang ialah keupayaan untuk mencipta kelas dengan berbilang kelas induk. Tidak seperti bahasa berorientasikan objek popular lain seperti C++, Java tidak menyokong warisan berbilang kelas. Dia tidak menyokongnya kerana kemungkinan menghadapi "masalah berlian" dan sebaliknya lebih suka menyediakan beberapa jenis pendekatan komprehensif untuk menyelesaikannya, menggunakan pilihan terbaik yang boleh kita capai hasil pewarisan yang serupa."Masalah Berlian"
Untuk memahami masalah berlian dengan lebih mudah, mari kita anggap bahawa warisan berbilang disokong di Jawa. Dalam kes ini, kita boleh mendapatkan kelas dengan hierarki yang ditunjukkan dalam rajah di bawah. Mari kita anggap ituSuperClass
ialah kelas abstrak yang menerangkan kaedah tertentu, dan kelas
ClassA
dan
ClassB
kelas sebenar.
SuperClass.java
package com.journaldev.inheritance;
public abstract class SuperClass {
public abstract void doSomething();
}
ClassA.java
package com.journaldev.inheritance;
public class ClassA extends SuperClass{
@Override
public void doSomething(){
System.out.println("Какая-то реализация класса A");
}
//собственный метод класса ClassA
public void methodA(){
}
}
Sekarang, mari kita anggap bahawa kelas
ClassC
mewarisi dari
ClassA
dan
ClassB
pada masa yang sama, dan pada masa yang sama mempunyai pelaksanaan berikut:
package com.journaldev.inheritance;
public class ClassC extends ClassA, ClassB{
public void test(){
//вызов метода родительского класса
doSomething();
}
}
Ambil perhatian bahawa kaedah
test()
memanggil kaedah
doSomething()
kelas induk, yang akan membawa kepada kekaburan kerana pengkompil tidak tahu kaedah superclass yang harus dipanggil. Oleh kerana bentuk rajah warisan kelas dalam situasi ini, yang menyerupai garis besar berlian segi, masalah itu dipanggil "Masalah Berlian". Inilah sebab utama mengapa Java tidak menyokong warisan kelas berbilang. Ambil perhatian bahawa masalah dengan warisan kelas berbilang juga boleh berlaku dengan tiga kelas yang mempunyai sekurang-kurangnya satu kaedah biasa.
Pewarisan berbilang dan antara muka
Anda mungkin perasan bahawa saya selalu mengatakan "warisan berbilang tidak disokong antara kelas", tetapi ia disokong antara antara muka. Contoh mudah ditunjukkan di bawah:InterfaceA.java
package com.journaldev.inheritance;
public interface InterfaceA {
public void doSomething();
}
InterfaceB.java
package com.journaldev.inheritance;
public interface InterfaceB {
public void doSomething();
}
Perhatikan bahawa kedua-dua antara muka mempunyai kaedah dengan nama yang sama. Sekarang katakan kita mempunyai antara muka yang mewarisi daripada kedua-dua antara muka.
InterfaceC.java
package com.journaldev.inheritance;
public interface InterfaceC extends InterfaceA, InterfaceB {
//метод, с тем же названием описан в InterfaceA и InterfaceB
public void doSomething();
Di sini, segala-galanya adalah ideal, kerana antara muka hanyalah tempahan/penerangan kaedah, dan pelaksanaan kaedah itu sendiri akan berada dalam kelas konkrit yang melaksanakan antara muka ini, jadi tidak ada kemungkinan menghadapi kekaburan dengan pelbagai warisan antara muka. Inilah sebabnya mengapa kelas di Java boleh mewarisi daripada berbilang antara muka. Mari tunjukkan dengan contoh di bawah.
InterfacesImpl.java
package com.journaldev.inheritance;
public class InterfacesImpl implements InterfaceA, InterfaceB, InterfaceC {
@Override
public void doSomething() {
System.out.println("doSomething реализация реального класса ");
}
public static void main(String[] args) {
InterfaceA objA = new InterfacesImpl();
InterfaceB objB = new InterfacesImpl();
InterfaceC objC = new InterfacesImpl();
//все вызываемые ниже методы получат одинаковую реализацию конкретного класса
objA.doSomething();
objB.doSomething();
objC.doSomething();
}
}
Anda mungkin perasan bahawa setiap kali saya mengatasi kaedah yang diterangkan dalam superclass atau dalam antara muka, saya menggunakan anotasi @Override. Ini adalah salah satu daripada tiga anotasi Java terbina dalam dan anda harus sentiasa menggunakannya apabila mengatasi kaedah.
Komposisi sebagai keselamatan
Jadi bagaimana jika kita mahu menggunakanmethodA()
kelas
ClassA
dan fungsi
methodB()
kelas
ClassB
dalam
ClassС
? Penyelesaian untuk ini boleh menjadi komposisi - versi yang ditulis semula
ClassC
yang melaksanakan kedua-dua kaedah kelas
ClassA
dan
ClassB
juga mempunyai pelaksanaan
doSomething()
untuk salah satu objek.
ClassC.java
package com.journaldev.inheritance;
public class ClassC{
ClassA objA = new ClassA();
ClassB objB = new ClassB();
public void test(){
objA.doSomething();
}
public void methodA(){
objA.methodA();
}
public void methodB(){
objB.methodB();
}
}
Komposisi atau warisan?
Amalan pengaturcaraan Java yang baik adalah untuk memanfaatkan komposisi berbanding warisan. Kami akan melihat beberapa aspek yang memihak kepada pendekatan ini.-
Katakan kita mempunyai gabungan kelas ibu bapa-waris berikut:
ClassC.java
package com.journaldev.inheritance; public class ClassC{ public void methodC(){ } }
ClassD.java
package com.journaldev.inheritance; public class ClassD extends ClassC{ public int test(){ return 0; } }
Kod di atas menyusun dan berfungsi dengan baik, tetapi bagaimana jika
ClassC
ia dilaksanakan secara berbeza:package com.journaldev.inheritance; public class ClassC{ public void methodC(){ } public void test(){ } }
Ambil perhatian bahawa kaedah itu
test()
sudah wujud dalam kelas keturunan, tetapi mengembalikan hasil daripada jenis yang berbeza. SekarangClassD
, sekiranya anda menggunakan IDE, ia tidak akan disusun. Anda akan dinasihatkan untuk menukar jenis pulangan dalam keturunan atau superclass.Sekarang mari kita bayangkan situasi di mana terdapat warisan kelas berbilang peringkat dan kelas super tidak tersedia untuk perubahan kita. Sekarang, untuk menghapuskan ralat penyusunan, kami tidak mempunyai pilihan lain selain menukar tandatangan atau nama kaedah subkelas. Kami juga perlu membuat perubahan pada semua tempat di mana kaedah ini dipanggil. Oleh itu, warisan menjadikan kod kami rapuh.
Masalah yang diterangkan di atas tidak pernah berlaku dalam kes komposisi, dan oleh itu menjadikan yang kedua lebih disukai daripada warisan.
-
Masalah seterusnya dengan warisan ialah kami mendedahkan semua kaedah ibu bapa kepada pelanggan. Dan jika superclass tidak direka dengan betul dan mengandungi lubang keselamatan. Kemudian, walaupun kami menjaga keselamatan sepenuhnya dalam pelaksanaan subkelas kami, kami masih bergantung pada pelaksanaan kelas induk yang cacat.
Komposisi membantu kami menyediakan akses terkawal kepada kaedah kelas super, manakala warisan tidak mengekalkan sebarang kawalan ke atas kaedahnya. Ini juga merupakan salah satu kelebihan utama komposisi berbanding warisan.
-
Satu lagi faedah komposisi ialah ia menambah fleksibiliti semasa memanggil kaedah. Pelaksanaan kelas
ClassC
yang diterangkan di atas tidak optimum dan menggunakan pengikatan awal kepada kaedah yang dipanggil. Perubahan minimum akan membolehkan kami membuat panggilan kaedah fleksibel dan membenarkan pengikatan lewat (mengikat pada masa jalan).ClassC.java
package com.journaldev.inheritance; public class ClassC{ SuperClass obj = null; public ClassC(SuperClass o){ this.obj = o; } public void test(){ obj.doSomething(); } public static void main(String args[]){ ClassC obj1 = new ClassC(new ClassA()); ClassC obj2 = new ClassC(new ClassB()); obj1.test(); obj2.test(); } }
Program di atas akan memaparkan:
doSomething implementation of A doSomething implementation of B
Fleksibiliti dalam panggilan kaedah ini tidak dilihat dengan warisan, yang menjadikan komposisi pendekatan terbaik.
-
Ujian unit adalah lebih mudah dalam kes komposisi kerana kita tahu bahawa untuk semua kaedah yang digunakan dalam superclass kita boleh stub ujian, manakala dalam warisan kita sangat bergantung pada superclass dan tidak tahu bagaimana kaedah kelas induk akan digunakan. Oleh itu, disebabkan warisan, kita perlu menguji semua kaedah superclass, yang merupakan kerja yang tidak perlu.
Sebaik-baiknya, warisan hanya boleh digunakan apabila perhubungan " is-a " adalah benar untuk kelas induk dan anak, jika tidak, komposisi harus diutamakan.
GO TO FULL VERSION