Panduan Warisan di Jawa

1. Ikhtisar

Salah satu prinsip inti Pemrograman Berorientasi Objek - pewarisan - memungkinkan kita menggunakan kembali kode yang ada atau memperluas tipe yang sudah ada.

Sederhananya, di Java, sebuah kelas dapat mewarisi kelas lain dan beberapa antarmuka, sementara sebuah antarmuka dapat mewarisi antarmuka lain.

Dalam artikel ini, kita akan mulai dengan kebutuhan akan warisan, beralih ke cara kerja warisan dengan kelas dan antarmuka.

Kemudian, kita akan membahas bagaimana nama variabel / metode dan pengubah akses memengaruhi anggota yang diwarisi.

Dan pada akhirnya, kita akan melihat apa artinya mewarisi suatu tipe.

2. Kebutuhan Warisan

Bayangkan, sebagai produsen mobil, Anda menawarkan beberapa model mobil kepada pelanggan Anda. Meskipun model mobil yang berbeda mungkin menawarkan fitur yang berbeda seperti jendela anti atap atau anti peluru, semuanya akan menyertakan komponen dan fitur umum, seperti mesin dan roda.

Masuk akal untuk membuat desain dasar dan mengembangkannya untuk membuat versi khusus mereka, daripada mendesain setiap model mobil secara terpisah, dari awal.

Dengan cara yang sama, dengan pewarisan, kita dapat membuat kelas dengan fitur dan perilaku dasar dan membuat versi khususnya, dengan membuat kelas, yang mewarisi kelas dasar ini. Dengan cara yang sama, antarmuka dapat memperluas antarmuka yang ada.

Kami akan melihat penggunaan beberapa istilah untuk merujuk ke jenis yang diwarisi oleh jenis lain, khususnya:

  • tipe dasar juga disebut super atau tipe induk
  • tipe turunan disebut sebagai diperpanjang, sub atau tipe anak

3. Warisan Kelas

3.1. Memperluas Kelas

Kelas dapat mewarisi kelas lain dan menentukan anggota tambahan.

Mari kita mulai dengan mendefinisikan mobil kelas dasar :

public class Car { int wheels; String model; void start() { // Check essential parts } }

Kelas ArmoredCar dapat mewarisi anggota kelas Car dengan menggunakan kata kunci extends dalam deklarasinya :

public class ArmoredCar extends Car { int bulletProofWindows; void remoteStartCar() { // this vehicle can be started by using a remote control } }

Sekarang kita dapat mengatakan bahwa kelas ArmoredCar adalah subkelas Mobil, dan yang terakhir adalah kelas super dari ArmoredCar.

Kelas di Java mendukung pewarisan tunggal ; yang ArmoredCar kelas tidak dapat memperpanjang beberapa kelas.

Juga, perhatikan bahwa dengan tidak adanya kata kunci extends , sebuah kelas secara implisit mewarisi kelas java.lang.Object .

Kelas subkelas mewarisi anggota dilindungi dan publik non-statis dari kelas superkelas. Selain itu, anggota dengan default dan akses paket diwariskan jika dua kelas berada dalam paket yang sama.

Di sisi lain, anggota privat dan statis kelas tidak diwariskan.

3.2. Mengakses Anggota Orang Tua dari Kelas Anak

Untuk mengakses properti atau metode yang diwariskan, kita cukup menggunakannya secara langsung:

public class ArmoredCar extends Car { public String registerModel() { return model; } }

Perhatikan bahwa kita tidak memerlukan referensi ke superclass untuk mengakses anggotanya.

4. Warisan Antarmuka

4.1. Menerapkan Banyak Antarmuka

Meskipun kelas hanya dapat mewarisi satu kelas, mereka dapat mengimplementasikan beberapa antarmuka.

Bayangkan ArmoredCar yang kami tentukan di bagian sebelumnya diperlukan untuk mata-mata super. Jadi, perusahaan manufaktur mobil berpikir untuk menambahkan fungsi terbang dan mengambang:

public interface Floatable { void floatOnWater(); }
public interface Flyable { void fly(); }
public class ArmoredCar extends Car implements Floatable, Flyable{ public void floatOnWater() { System.out.println("I can float!"); } public void fly() { System.out.println("I can fly!"); } }

Dalam contoh di atas, kami melihat penggunaan implementasi kata kunci untuk mewarisi dari sebuah antarmuka.

4.2. Masalah Dengan Banyak Warisan

Java memungkinkan banyak pewarisan menggunakan antarmuka.

Hingga Java 7, ini bukan masalah. Antarmuka hanya dapat mendefinisikan metode abstrak , yaitu metode tanpa implementasi apa pun. Jadi jika sebuah kelas menerapkan beberapa antarmuka dengan tanda tangan metode yang sama, itu tidak menjadi masalah. Kelas pelaksana akhirnya hanya memiliki satu metode untuk diimplementasikan.

Mari kita lihat bagaimana persamaan sederhana ini berubah dengan pengenalan metode default di antarmuka, dengan Java 8.

Dimulai dengan Java 8, antarmuka dapat memilih untuk mendefinisikan implementasi default untuk metodenya (sebuah antarmuka masih dapat mendefinisikan metode abstrak ). Ini berarti bahwa jika sebuah kelas mengimplementasikan beberapa antarmuka, yang mendefinisikan metode dengan tanda tangan yang sama, kelas turunan akan mewarisi implementasi terpisah. Ini terdengar rumit dan tidak diperbolehkan.

Java melarang pewarisan beberapa implementasi dari metode yang sama, yang ditentukan dalam antarmuka terpisah.

Berikut contohnya:

public interface Floatable { default void repair() { System.out.println("Repairing Floatable object"); } }
public interface Flyable { default void repair() { System.out.println("Repairing Flyable object"); } }
public class ArmoredCar extends Car implements Floatable, Flyable { // this won't compile }

Jika kita ingin mengimplementasikan kedua antarmuka, kita harus mengganti metode repair () .

Jika antarmuka dalam contoh sebelumnya mendefinisikan variabel dengan nama yang sama, katakan durasi , kita tidak dapat mengaksesnya tanpa mendahului nama variabel dengan nama antarmuka:

public interface Floatable { int duration = 10; }
public interface Flyable { int duration = 20; }
public class ArmoredCar extends Car implements Floatable, Flyable { public void aMethod() { System.out.println(duration); // won't compile System.out.println(Floatable.duration); // outputs 10 System.out.println(Flyable.duration); // outputs 20 } }

4.3. Interfaces Extending Other Interfaces

An interface can extend multiple interfaces. Here's an example:

public interface Floatable { void floatOnWater(); }
interface interface Flyable { void fly(); }
public interface SpaceTraveller extends Floatable, Flyable { void remoteControl(); }

An interface inherits other interfaces by using the keyword extends. Classes use the keyword implements to inherit an interface.

5. Inheriting Type

When a class inherits another class or interfaces, apart from inheriting their members, it also inherits their type. This also applies to an interface that inherits other interfaces.

This is a very powerful concept, which allows developers to program to an interface (base class or interface), rather than programming to their implementations.

For example, imagine a condition, where an organization maintains a list of the cars owned by its employees. Of course, all employees might own different car models. So how can we refer to different car instances? Here's the solution:

public class Employee { private String name; private Car car; // standard constructor }

Because all derived classes of Car inherit the type Car, the derived class instances can be referred by using a variable of class Car:

Employee e1 = new Employee("Shreya", new ArmoredCar()); Employee e2 = new Employee("Paul", new SpaceCar()); Employee e3 = new Employee("Pavni", new BMW());

6. Hidden Class Members

6.1. Hidden Instance Members

What happens if both the superclass and subclass define a variable or method with the same name? Don't worry; we can still access both of them. However, we must make our intent clear to Java, by prefixing the variable or method with the keywords this or super.

The this keyword refers to the instance in which it's used. The super keyword (as it seems obvious) refers to the parent class instance:

public class ArmoredCar extends Car { private String model; public String getAValue() { return super.model; // returns value of model defined in base class Car // return this.model; // will return value of model defined in ArmoredCar // return model; // will return value of model defined in ArmoredCar } }

A lot of developers use this and super keywords to explicitly state which variable or method they're referring to. However, using them with all members can make our code look cluttered.

6.2. Hidden Static Members

What happens when our base class and subclasses define static variables and methods with the same name? Can we access a static member from the base class, in the derived class, the way we do for the instance variables?

Let's find out using an example:

public class Car { public static String msg() { return "Car"; } }
public class ArmoredCar extends Car { public static String msg() { return super.msg(); // this won't compile. } }

No, we can't. The static members belong to a class and not to instances. So we can't use the non-static super keyword in msg().

Since static members belong to a class, we can modify the preceding call as follows:

return Car.msg();

Consider the following example, in which both the base class and derived class define a static method msg() with the same signature:

public class Car { public static String msg() { return "Car"; } }
public class ArmoredCar extends Car { public static String msg() { return "ArmoredCar"; } }

Here's how we can call them:

Car first = new ArmoredCar(); ArmoredCar second = new ArmoredCar();

For the preceding code, first.msg() will output “Car and second.msg() will output “ArmoredCar”. The static message that is called depends on the type of the variable used to refer to ArmoredCar instance.

7. Conclusion

Pada artikel ini, kami membahas aspek inti dari bahasa Java - warisan.

Kami melihat bagaimana Java mendukung pewarisan tunggal dengan kelas dan beberapa pewarisan dengan antarmuka dan membahas seluk-beluk cara kerja mekanisme dalam bahasa tersebut.

Seperti biasa, kode sumber lengkap untuk contoh tersedia di GitHub.