Hai! Hari ini kita akan melihat mekanisme penting: warisan dalam kelas bersarang. Pernahkah anda terfikir tentang perkara yang akan anda lakukan jika anda perlu menjadikan kelas bersarang mewarisi kelas lain. Jika tidak, percayalah: keadaan ini boleh mengelirukan, kerana terdapat banyak nuansa.
Peraturan pewarisan mereka adalah yang paling mudah. Di sini anda boleh melakukan hampir apa sahaja yang anda inginkan. Kelas bersarang statik boleh mewarisi:
Sekali lagi, mari kita beralih dari mudah kepada kompleks :)
- Adakah kita membuat kelas bersarang mewarisi beberapa kelas? Atau adakah kita menjadikan sesetengah kelas mewarisi kelas bersarang?
- Adakah kelas anak/ibu bapa kelas awam biasa, atau adakah ia juga kelas bersarang?
- Akhir sekali, apakah jenis kelas bersarang yang kami gunakan dalam semua situasi ini?
Kelas bersarang statik

- kelas biasa
- kelas bersarang statik yang diisytiharkan dalam kelas luar atau nenek moyangnya
public class Boeing737 {
private int manufactureYear;
private static int maxPassengersCount = 300;
public Boeing737(int manufactureYear) {
this.manufactureYear = manufactureYear;
}
public int getManufactureYear() {
return manufactureYear;
}
public static class Drawing {
public static int getMaxPassengersCount() {
return maxPassengersCount;
}
}
}
Mari cuba tukar kod dan buat Drawing
kelas bersarang statik dan keturunannya — Boeing737Drawing
.
public class Boeing737 {
private int manufactureYear;
private static int maxPassengersCount = 300;
public Boeing737(int manufactureYear) {
this.manufactureYear = manufactureYear;
}
public int getManufactureYear() {
return manufactureYear;
}
public static class Drawing {
}
public static class Boeing737Drawing extends Drawing {
public static int getMaxPassengersCount() {
return maxPassengersCount;
}
}
}
Seperti yang anda lihat, tiada masalah. Kita juga boleh menarik keluar Drawing
kelas dan menjadikannya kelas awam biasa dan bukannya kelas bersarang statik — tiada apa yang akan berubah.
public class Drawing {
}
public class Boeing737 {
private int manufactureYear;
private static int maxPassengersCount = 300;
public Boeing737(int manufactureYear) {
this.manufactureYear = manufactureYear;
}
public int getManufactureYear() {
return manufactureYear;
}
public static class Boeing737Drawing extends Drawing {
public static int getMaxPassengersCount() {
return maxPassengersCount;
}
}
}
Kami faham ini. Tetapi kelas apakah yang boleh mewarisi kelas bersarang statik? Boleh dikatakan mana-mana! Bersarang/tidak bersarang, statik/tidak statik — tidak mengapa. Di sini kita menjadikan Boeing737Drawing
kelas dalam mewarisi Drawing
kelas bersarang statik:
public class Boeing737 {
private int manufactureYear;
private static int maxPassengersCount = 300;
public Boeing737(int manufactureYear) {
this.manufactureYear = manufactureYear;
}
public int getManufactureYear() {
return manufactureYear;
}
public static class Drawing {
}
public class Boeing737Drawing extends Drawing {
public int getMaxPassengersCount() {
return maxPassengersCount;
}
}
}
Anda boleh membuat contoh Boeing737Drawing
seperti ini:
public class Main {
public static void main(String[] args) {
Boeing737 boeing737 = new Boeing737(1990);
Boeing737.Boeing737Drawing drawing = boeing737.new Boeing737Drawing();
System.out.println(drawing.getMaxPassengersCount());
}
}
Walaupun Boeing737Drawing
kelas kami mewarisi kelas statik, ia bukan statik sendiri! Akibatnya, ia akan sentiasa memerlukan contoh kelas luar. Kita boleh mengalih keluar Boeing737Drawing
kelas daripada Boeing737
kelas dan menjadikannya kelas awam yang mudah. Tiada perubahan. Ia masih boleh mewarisi Drawing
kelas bersarang statik.
public class Boeing737 {
private int manufactureYear;
public static int maxPassengersCount = 300;
public Boeing737(int manufactureYear) {
this.manufactureYear = manufactureYear;
}
public int getManufactureYear() {
return manufactureYear;
}
public static class Drawing {
}
}
public class Boeing737Drawing extends Boeing737.Drawing {
public int getMaxPassengersCount() {
return Boeing737.maxPassengersCount;
}
Satu-satunya perkara penting ialah dalam kes ini kita perlu membuat maxPassengersCount
pembolehubah statik awam. Jika ia kekal peribadi, maka kelas awam biasa tidak akan mempunyai akses kepadanya. Kami telah mengetahui kelas statik! :) Sekarang mari kita beralih ke kelas dalaman. Mereka datang dalam 3 jenis: kelas dalaman ringkas, kelas tempatan dan kelas dalaman tanpa nama. 
Kelas dalaman tanpa nama
Kelas dalaman tanpa nama tidak boleh mewarisi kelas lain. Tiada kelas lain boleh mewarisi kelas tanpa nama. Ia tidak boleh menjadi lebih mudah! :)Kelas tempatan
Kelas tempatan (sekiranya anda terlupa) diisytiharkan di dalam blok kod kelas lain. Selalunya, ini berlaku dalam beberapa kaedah kelas luar. Secara logiknya, hanya kelas tempatan lain dalam kaedah yang sama (atau blok kod) boleh mewarisi kelas tempatan. Berikut adalah contoh:
public class PhoneNumberValidator {
public void validatePhoneNumber(final String number) {
class PhoneNumber {
private String phoneNumber;
public PhoneNumber() {
this.phoneNumber = number;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
class CellPhoneNumber extends PhoneNumber {
}
class LandlinePhoneNumber extends PhoneNumber {
}
// ...number validation code
}
}
Ini adalah kod daripada pelajaran kami tentang kelas tempatan. Kelas pengesah nombor kami mempunyai PhoneNumber
kelas tempatan. Jika kami memerlukannya untuk mewakili dua entiti yang berbeza, contohnya, nombor telefon mudah alih dan nombor telefon talian tetap, kami hanya boleh melakukan ini dalam kaedah yang sama. Sebabnya mudah: skop kelas tempatan terhad kepada kaedah (blok kod) di mana ia diisytiharkan. Akibatnya, kami tidak akan dapat menggunakannya secara luaran (termasuk untuk warisan kelas). Walau bagaimanapun, kemungkinan untuk warisan dalam kelas tempatan itu sendiri adalah lebih luas! Kelas tempatan boleh mewarisi:
- Kelas biasa.
- Kelas dalaman yang diisytiharkan dalam kelas yang sama dengan kelas tempatan atau dalam salah satu nenek moyangnya.
- Satu lagi kelas tempatan diisytiharkan dalam kaedah yang sama (blok kod).
public class PhoneNumberValidator {
class PhoneNumber {
private String phoneNumber;
public PhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
public void validatePhoneNumber(final String number) {
class CellPhoneNumber extends PhoneNumber {
public CellPhoneNumber(String phoneNumber) {
super(number);
}
}
class LandlinePhoneNumber extends PhoneNumber {
public LandlinePhoneNumber(String phoneNumber) {
super(number);
}
}
// ...number validation code
}
}
Di sini kami mengalih keluar PhoneNumber
kelas daripada validatePhoneNumber()
kaedah dan menjadikannya kelas dalaman dan bukannya kelas tempatan. Ini tidak menghalang kami daripada menjadikan 2 kelas tempatan kami mewarisinya. Contoh 2 — "... atau dalam nenek moyang kelas ini." Sekarang ini sudah lebih menarik. Kita boleh bergerak PhoneNumber
lebih tinggi lagi dalam rantaian warisan. Mari kita isytiharkan AbstractPhoneNumberValidator
kelas abstrak, yang akan menjadi moyang PhoneNumberValidator
kelas kita:
public abstract class AbstractPhoneNumberValidator {
class PhoneNumber {
private String phoneNumber;
public PhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
}
Seperti yang anda lihat, kami bukan sahaja mengisytiharkannya — kami juga memindahkan PhoneNumber
kelas dalaman ke dalamnya. Walau bagaimanapun, dalam keturunannya PhoneNumberValidator
, kelas tempatan yang diisytiharkan dalam kaedah boleh mewarisi PhoneNumber
tanpa sebarang masalah!
public class PhoneNumberValidator extends AbstractPhoneNumberValidator {
public void validatePhoneNumber(final String number) {
class CellPhoneNumber extends PhoneNumber {
public CellPhoneNumber(String phoneNumber) {
super(number);
}
}
class LandlinePhoneNumber extends PhoneNumber {
public LandlinePhoneNumber(String phoneNumber) {
super(number);
}
}
// ...number validation code
}
}
Disebabkan hubungan warisan, kelas tempatan dalam kelas keturunan "melihat" kelas dalaman di dalam moyang. Dan akhirnya, mari kita teruskan ke kumpulan terakhir :)
Kelas dalaman
Kelas dalam yang diisytiharkan dalam kelas luar yang sama (atau dalam keturunannya) boleh mewarisi kelas dalam yang lain. Mari kita terokai ini menggunakan contoh kita dengan basikal daripada pelajaran tentang kelas dalaman.
public class Bicycle {
private String model;
private int maxWeight;
public Bicycle(String model, int maxWeight) {
this.model = model;
this.maxWeight = maxWeight;
}
public void start() {
System.out.println("Let's go!");
}
class Seat {
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
class SportSeat extends Seat {
// ...methods
}
}
Di sini kami mengisytiharkan Seat
kelas dalaman di dalam Bicycle
kelas. Jenis kerusi perlumbaan istimewa, SportSeat
, mewarisinya. Tetapi, kita boleh mencipta jenis "basikal lumba" yang berasingan dan meletakkannya dalam kelas yang berasingan:
public class SportBicycle extends Bicycle {
public SportBicycle(String model, int maxWeight) {
super(model, maxWeight);
}
class SportSeat extends Seat {
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
}
Ini juga satu pilihan. Kelas dalaman keturunan ( SportBicycle.SportSeat
) "melihat" kelas dalaman nenek moyang dan boleh mewarisi mereka. Mewarisi kelas dalaman mempunyai satu ciri yang sangat penting! Dalam dua contoh sebelum ini, SportSeat
kelas kami ialah kelas dalaman. Tetapi bagaimana jika kita memutuskan untuk membuat SportSeat
kelas awam biasa yang secara serentak mewarisi Seat
kelas dalaman?
// Error! No enclosing instance of type 'Bicycle' is in scope
class SportSeat extends Bicycle.Seat {
public SportSeat() {
}
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
Kami mendapat ralat! Boleh teka kenapa? :) Semuanya mudah. Apabila kita bercakap tentang Bicycle.Seat
kelas dalam, kita menyebut bahawa rujukan kepada contoh kelas luar secara tersirat dihantar kepada pembina kelas dalam. Ini bermakna anda tidak boleh mencipta Seat
objek tanpa mencipta Bicycle
objek. Tetapi bagaimana dengan penciptaan sebuah SportSeat
? Tidak seperti Seat
, ia tidak mempunyai mekanisme terbina dalam ini untuk secara tersirat memberikan rujukan kepada pembina kepada contoh kelas luar. S sehingga, tanpa Bicycle
objek, kita tidak boleh mencipta SportSeat
objek, sama seperti dalam kes Seat
. Oleh itu, hanya ada satu perkara yang tinggal untuk kita lakukan - secara eksplisit menyampaikan kepada SportSeat
pembina rujukan kepada Bicycle
objek. Begini cara melakukannya:
class SportSeat extends Bicycle.Seat {
public SportSeat(Bicycle bicycle) {
bicycle.super();
}
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
Kami memanggil pembina superclass menggunakan super();
Now, jika kami ingin mencipta SportSeat
objek, tiada apa yang akan menghalang kami daripada melakukan ini:
public class Main {
public static void main(String[] args) {
Bicycle bicycle = new Bicycle("Peugeot", 120);
SportSeat peugeotSportSeat = new SportSeat(bicycle);
}
}
Fuh! Pelajaran ini agak panjang :) Tetapi anda belajar banyak! Kini tiba masanya untuk menyelesaikan beberapa tugas! :)
GO TO FULL VERSION