Java

Inheritance in Java

Xin chào, trong bài viết này chúng ta sẽ tìm hiểu về Kế thừa (Inheritance) trong Java.

Kế thừa là một trong những nền tảng của lập trình hướng đối tượng (OOP), nó cho phép một đối tượng được thừa hưởng các thuộc tính từ một đối tượng khác, nhờ đó mà chương trình sẽ linh hoạt hơn cũng như hạn chế trùng lặp code.

Trong Java, class được kế thừa bởi một class khác được gọi là superclass, class thừa hưởng các thuộc tính từ một class khác được gọi là subclass.


INHERITANCE BASICS

Để kế thừa một class trong Java chúng ta sử dụng keyword extends. Subclass có thể truy cập tất cả các member của superclass (ngoại trừ các members được khai báo privatemà không cần khai báo nào khác.

class subclass_name extends superclass_name { // body of subclass}

Example 1:

class Animal {
    void eat() {
        System.out.println("Eating");
    }
    void sleep() {
        System.out.println("Sleeping");
    }
}

class Dog extends Animal {
    void bark() {
        System.out.println("Woof woof!");
    }
}

public class test {
    public static void main(String[] args) {
        Dog dog_1 = new Dog();
        Animal dog_2 = new Dog();
        System.out.println("Dog_1:");
        dog_1.eat();
        dog_1.sleep();
        dog_1.bark();

        System.out.println("Dog_2:");
        dog_2.eat();
        dog_2.sleep();
        // dog_2.bark(); // ERROR!
    }
}

Output:
Dog_1:
Eating
Sleeping
Gau gau!
Dog_2:
Eating
Sleeping

Giải thích:
– class Animal chính là superclass, được kế thừa bởi subclass Dog;
– mặc dù trong subclass Dog chúng ta chỉ khai báo thêm function bark() nhưng chúng ta vẫn có thể gọi các function eating() và sleeping(), bởi vì subclass Dog được thừa hưởng chúng từ superclass Animal;
một điều đặc biệt: một biến có kiểu superclass (superclass reference) có thể tham khảo đến instance của bất cứ subclass nào kế thừa từ superclass đó, trong ví dụ trên là dòng Animal dog_2 = new Dog(). Tuy nhiên, vì biến dog_2 có kiểu dữ liệu là Animal nên nó chỉ có thể truy cập đến các member trong subclass Dog mà được kế thừa từ superclass Animal, ở ví dụ trên là 2 methods eat() và sleep().


KEYWORD SUPER

Từ bên trong subclass, super được dùng để tham khảo đến superclass. Từ khóa super được sử dụng ở 2 dạng: gọi superclass’ constructor và truy cập đến member của superclass từ subclass.

Using super to call superclass’ constructors

Subclass có thể gọi superclass’ constructor bằng super:

super(args_list)

Trong đó: args_list là các arguments được yêu cầu bởi superclass’ constructor.

Chú ý:

  • Function super() luôn luôn phải là câu lệnh đầu tiên được thực thi trên trong subclass’ constructor;
  • Khi super() được sử dụng, số lượng tham số được truyền vào super() phải tương ứng với superclass’ constructor;
  • Nếu super() không được sử dụng thì default constructor (không có tham số) của superclass sẽ được gọi.

Example 2:

class Vehicle {
    String type;

    Vehicle(String _type) {
        type = _type;
    }

    Vehicle() {
        type = "vehicle";
    }

    void info() {
        System.out.println("This is a "+type);
    }
}

class Motocircle extends Vehicle {
    String brand;

    Motocircle(String _type, String _brand) {
        super(_type);
        brand = _brand;
    }

    void info() {
        super.info();
        System.out.println("Brand: "+brand);
    }
}

class Automobile extends Vehicle {
    String brand;

    Automobile(String _brand) {
        brand = _brand;
    }

    void info() {
        super.info();
        System.out.println("Brand: "+brand);
    }
}

public class test {
    public static void main(String[] args) {
        Automobile a = new Automobile("Audi");
        Motocircle m = new Motocircle("motocircle", "Honda");

        a.info();
        m.info();
    }
}

Output:
This is a vehicle
Band: Audi
This is a motocircle
Brand: Honda

Giải thích:
– class Vehicle có 2 constructor là Vehicle(String) và Vehicle() – default constructor;
– bên trong constructor của subclass Motocircle chúng ta sử dụng super(_type) để gọi constructor Vehicle(String) của superclass Vehicle;
– bên trong constructor của subclass Automobile chúng ta không sử dụng super() nên default constructor Vehicle() sẽ được gọi.

Using super to access superclass’ members

Trong trường hợp này, keyword super sẽ có chắc năng tương tự với keyword this, tuy nhiên super sẽ tham khảo đến superclass.

super.member

Trong example 2 phía trên, chúng ta đã dùng super.info() để tham khảo đến method info () của superclass . Ở example 3 chúng ta sẽ tìm hiểu về cách truy cập biến của superclass.

Example 3:

class A {
    int i;
}

class B extends A {
    int i;
    B(int a, int b) {
        super.i = a;
        i = b;
    }

    void show() {
        System.out.println("i in superclass: "+super.i);
        System.out.println("i in subclass: "+i);
    }
}

public class test {
    public static void main(String[] args) {
        B subOb = new B(1,2);
        subOb.show();
    }
}

Output:
i in superclass: 1
i in subclass: 2

Giải thích:
– biến i trong được khai báo trong subclass B sẽ thay thế cho biến i mà subclass B được kế thừa thừ superclass A. Do đó, để truy cập vào biến i của superclass A, chúng ta cần sử dụng super.i.


METHOD OVERRIDING

Khi một method của subclass có cùng tên, kiểu dữ liệu trả về và các tham số với một method của superclass, thì method trong subclass sẽ override method trong superclass.

Cần chú ý phân biệt giữa overridding và overloading:

  1. Overriding: hai method phải có tên, kiểu dữ liệu và danh sách tham số hoàn toàn giống nhau. Overridden method trong subclass sẽ được gọi thay vì method của superclass;
  2. Overloading: hai method chỉ cần có chung tên. Tạo thêm nhiều phiên bản của một method, phiên bản phù hợp sẽ được thực thi dựa trên kiểu dữ liệu trả về và danh sách tham số.

Example 4:

class Vehicle {
    String type;

    Vehicle(String _type) {
        type = _type;
    }

    Vehicle() {
       type = "vehicle";
    }

    void info() {
        System.out.println("This is a "+type);
    }

    void run() {
        System.out.println("Brum brum");
    }
}

class Motocircle extends Vehicle {
    String brand;

    Motocircle(String _type, String _brand) {
        super(_type);
        brand = _brand;
    }

    void info() {
        super.info();
        System.out.println("Brand: "+brand);
    }

    void run() {
        System.out.println("It can run at 60 km/h");
    }
}

public class test {
    public static void main(String[] args) {
        Motocircle m = new Motocircle("motocircle", "Honda");

        m.info();
        m.run();
    }
}

Output:
This is a motocircle
Brand: Honda
It can run at 60 km/h

Giải thích:
– trong subclass motocircle, chúng ta đã override method info() và method run(). Trong method info(), chúng ta sử dụng super.info() để gọi method info() của superclass Vehicle.


DYNAMIC METHOD DISPATCH

Ở example 4 chúng ta đã tìm hiểu về cách override một method, tuy nhiên tại sao chúng ta cần method overidding?

Dynamic method dispatch là một cơ chế cho phép sử dụng đúng overridden method tùy vào từng subclass tại run time.  Từ example 1 chúng ta đã biết rằng một biến có kiểu superclass (superclass reference) có thể tham khảo đến một instance của bất kỳ subclass nào kế thừa từ superclass đó, khi một overridden method được gọi bởi một superclass reference, Java sẽ xác định phiên bản cần được thực thi dựa trên kiểu dữ liệu của đối tượng đang được tham khảo đến tại thời điểm hiện tại. Nói cách khác, kiểu dữ liệu của đối tượng đang được tham khảo sẽ xác định phiên bản nào của overridden method sẽ được thực thi.

Example 5:

class Vehicle {
    String type;

    Vehicle(String _type) {
        type = _type;
    }

    Vehicle() {
       type = "vehicle";
    }

    void info() {
        System.out.println("This is a "+type);
    }

    void run() {
        System.out.println("Brum brum");
    }
}

class Motocircle extends Vehicle {
    String brand;

    Motocircle(String _type, String _brand) {
        super(_type);
        brand = _brand;
    }

    void info() {
        super.info();
        System.out.println("Brand: "+brand);
    }

    void run() {
        System.out.println("It can run at 60 km/h");
    }
}

class Automobile extends Vehicle {
    String brand;

    Automobile(String _brand) {
        brand = _brand;
    }

    void info() {
        super.info();
        System.out.println("Brand: "+brand);
    }

    void run() {
        super.run();
        System.out.println("It can run at 120 km/h");
    }
}

public class test {

    public static void main(String[] args) {
        Automobile a = new Automobile("Audi");
        Motocircle m = new Motocircle("motocircle", "Honda");
        Vehicle v = a;
        v.info();
        v.run();

        v = m;
        v.info();
        v.run();
    }
}

Output:
This is a vehicle
Brand: Audi
Brum brum
It can run at 120 km/h
This is a motocircle
Brand: Honda
It can run at 60 km/h

Giải thích:
– biến v có kiểu dữ liệu Vehicle, tham khảo đến biến a (thuộc kiểu Automobile) và m (thuộc kiểu Motocircle). Các phiên bản của method info() và run() được gọi từ biến v đều được xác định dựa trên kiểu dữ liệu của biến (a và m) mà v đang tham khảo đến.

Trả lời cho câu hỏi “Tại sao cần Method Overridding?“: các overridden methods cho phép Java hỗ trợ run-time polymorphism (Đa hình). Polymorphism là bản chất của OOP, nó cho phép một superclass chỉ định các method mà các subclass sẽ được kế thừa, trong khi đó cho phép các subclass chỉnh sửa các method này cho phù hợp (overridding) với từng hoàn cảnh. Như vậy với method overridding, Java có thể thực hiện “one interface, multiple methods”, điều này được thể hiện rõ ở example 5, khi mà một biến Vehicle v có thể gọi nhiều phiên bản khác nhau của các method info() và run().


USING ABSTRACT CLASSES

Sẽ có những trường hợp mà chúng ta muốn superclass chỉ khai báo “bộ khung” để các subclass có thể kế thừa, còn việc lấp đầy “bộ khung” sẽ do các subclass tự thực hiện. Bởi vì superclass không thể áp dụng một method implementation cho tất cả các method của các subclass, nên nó sẽ để trống phần implementation của method đó cho các subclass tự hoàn thiện. Java gọi nó là abstract method.

abstract type methodName(parameter_list);

Một class có chứa 1 hoặc nhiều abstract method phải được khai báo là abstract. Abstract class sẽ không thể có instance, bởi vì nó chưa được định nghĩa đầy đủ. Bất cứ subclass nào của abstract class đều bắt buộc phải định nghĩa cho các abstract methods của superclass hoặc chính nó phải được khai báo là abstract class.

Example 6:

abstract class Vehicle {
    String type;

    Vehicle(String _type) {
        type = _type;
    }

    Vehicle() {
       type = "vehicle";
    }

    void info() {
        System.out.println("This is a "+type);
    }

    abstract void engine(int capacity);
}

class Motocircle extends Vehicle {
    String brand;

    Motocircle(String _type, String _brand) {
        super(_type);
        brand = _brand;
    }

    void info() {
        super.info();
        System.out.println("Brand: "+brand);
    }

    void engine(int capacity) {
        System.out.println("Cylinder capacity: "+capacity+"cc");
    }
}

abstract class Automobile extends Vehicle {
    String brand;

    Automobile(String _brand) {
        brand = _brand;
    }

    void info() {
        super.info();
        System.out.println("Brand: "+brand);
    }
}

public class test {
    public static void main(String[] args) {
        Motocircle m1 = new Motocircle("moto", "Honda");
        Motocircle m2 = new Motocircle("moto", "Yamaha");
        Vehicle v = m2;
        m1.info();
        m1.engine(125);
        v.info();
        v.engine(150);
    }
}

Output:
This is a moto
Brand: Honda
Cylinder capacity: 125cc
This is a moto
Brand: Yamaha
Cylinder capacity: 150cc

Giải thích:
– chúng ta có abstract class Vehicle, chứa abstract method engine(). Subclass Motocircle kế thừa từ superclass Vehicle và đã định nghĩa cho abstract method engine();
– Subclass Automobile mặc dù kế thừa từ superclass Vehicle nhưng không cung cấp định nghĩa cho abstract method engine() nên vẫn phải khai báo là abstract class.
– Mặc dù abstract class Vehicle không thể có instance nhưng nó vẫn có thể được sử dụng để tạo superclass reference nhằm tham khảo đến các instance của subclass.


USING FINAL WITH INHERITANCE

Keyword final có 3 mục đích sử dụng, một là để khai báo constant, hai là để ngăn chặn method overriding và cuối cùng là để ngăn chặn kế thừa class.

Để ngăn chặn một method của superclass khỏi bị kế thừa bởi subclass, chúng ta sử dụng keyword final khi khai báo nó trong superclass.

Example 7: Prevent method overridding with final

class A {
    final void meth() {
        System.out.println("This is a final method.");
    }
}

class B extends A {
    void meth() {
        System.out.println("Illegal!");
    }
}

public class test {
    public static void main(String[] args) {
        A a = new A();
        B b = new B();
        a.meth();
        b.meth();
    }
}

Output:
Các bạn hãy chạy đoạn code trên và tự đưa ra kết luận nhé.

Để ngăn chăn một class bị kế thừa, chúng ta sử dụng keyword final khi khai báo class. Lúc này tất cả các method của nó cũng sẽ được ngầm khai báo là final.

Example 8: Prevent class inheritance with final

final class A {
    void meth() {
        System.out.println("This is a final class.");
    }
}

class B extends A {
    void meth() {
        System.out.println("Illegal!");
    }
}

public class test {
    public static void main(String[] args) {
        A a = new A();
        B b = new B();
        a.meth();
        b.meth();
    }
}

THE OBJECT CLASS

Trong Java có một class đặc biệt, đó là class Object. Mọi class khác do người dùng tạo đều là subclass của Object. Điều đó có nghĩa là chúng ta có thể dùng một biến kiểu Object để tham khảo đến instance của bất kỳ class nào, kể cả array (trong Java, array được triển khai như một class).

Class Object có các method sau: Capture

Trong đó, các method như getClass(), notify(), notifyAll() và wait() được khai báo final.

Method toString() trả về một String miêu tả về đối tượng và được tự động thực thi khi có một object được truyền vào function println(). Chúng ta có thể override method toString() để cung cấp mô tả chi tiết hơn về đối tượng, nếu cần thiết.

Example 9: overridding toString() method

class Vehicle {
    String type;

    Vehicle(String _type) {
        type = _type;
    }

    Vehicle() {
       type = "vehicle";
    }

    void info() {
        System.out.println("This is a "+type);
    }
}

class Motocircle extends Vehicle {
    String brand;

    Motocircle(String _type, String _brand) {
        super(_type);
        brand = _brand;
    }

    void info() {
        super.info();
        System.out.println("Brand: "+brand);
    }

    void engine(int capacity) {
        System.out.println("Cylinder capacity: "+capacity+"cc");
    }
}

class Automobile extends Vehicle {
    String brand;

    Automobile(String _brand) {
        brand = _brand;
    }

    void info() {
        super.info();
        System.out.println("Brand: "+brand);
    }

    public String toString() {
        return "This is class Automobile.";
    }
}

public class test {
    public static void main(String[] args) {
        Motocircle m = new Motocircle("Moto", "Honda");
        Automobile a = new Automobile("Audi");

        System.out.println(m);
        System.out.println(a);
    }
}

Output:
Motocircle@15db9742
This is class Automobile.

Giải thích:
– trong subclass Motocircle, chúng ta không hề đề cập đến method toString(), do đó, khi function println(object) được gọi, method toString() mặc định của superclass Object sẽ được thực thi;
– trong subclass Automobile, chúng ta đã override method toString(), do đó khi function println(object) được gọi, overridden method toString() của subclass Automobile sẽ được thực thi.


SUMMARY

Qua bài viết này chúng ta đã tìm hiểu khá nhiều thứ về kế thừa trong Java. Cảm ơn các bạn đã theo dõi bài viết.

Thân ái và quyết thắng.

Reference:
[1] Java – The Complete Reference. Ninth Edition.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s