Hướng đối tượng trong Java – part II

Trong phần này, chúng ta tiếp tục tìm hiểu về OOP trong Java gồm có: class trừu tượng (abstract classes), phương thức trừu tượng (abstract methods), interface, đa hình và một số loại class lồng nhau.

Abstract methods và Abstract class

Thiết kế ứng dụng, chúng ta thường thấy thằng các class thường có rất nhiều chức năng chung. Và các chức năng này được tách và đưa vào parent class. Điều này giúp giảm kích thước code và tạo ra ứng dụng gọn nhẹ hơn. Và chúng ta có thể cảm thấy rằng parent class là phi thực thể – chỉ nằm trên ý tưởng. Chúng ta có cây bút, quyển sách, bút chì, tách trà trên bàn. Một item có thể được xem như là parent class cho tất cả các vật này. Class này có một vài đặc điểm chung của các vật này. Ví dụ: id, trọng lượng hoặc màu sắc. Chúng ta có thể implement phương thức getId(), nhưng chúng ta không thể implement phương thức getWeight() hoặc getColor() trong parent class vì item có thể không có thuộc tính trọng lượng và màu sắc. Các phương thức này có thể được implement chỉ trong subclass của mỗi class.
Đối với những tình huống này, chúng ta có abstract method và abstract class.

Một abstract class hoặc abstract method được tạo bằng từ khóa abstract. Abstract classes không tạo được object nhưng có thể subclass. Nếu class bao gồm ít nhất 1 abstract method, nó phải được khai báo abstract. Đối với abstract method, chúng ta không implment, mà chỉ khai báo signature của phương thức (tên phương thức, kiểu dữ liệu tham số và kiểu trả về). Khi chúng ta kế thừa từ abstract class, tất cả các abstract method phải được implement trong class dẫn xuất.

Abstract class có thể có các phương thức được implement (gồm có body) và có thể các member. Các lập trình viên thường đặt các chức năng chung vào abstract class. Sau đó, các abstract class này sẽ được subclass và implement thêm các chức năng riêng. Các đặc tính chung được implement trong abstract class. Ví dụ: thư viện đồ họa Qt có abstract class ‘QAbstractButton’ của widget button, cung cấp các function chung cho button. Các class ‘Q3Button’, ‘QCheckBox’, ‘QPushButton’, ‘QRadioButton’, và ‘QToolButton’ kế thừa từ abstract class ‘QAbstractButton’. Các subclass này cung cấp function đặc chưng cho mỗi button.

Chú ý: Các phương thức static, private, final không được là abstract, vì các loại phương thức này không cho phép override bởi subclass. Tương tự, final class không thể có abstract method.

Ví dụ 1: Minh họa sử dụng abstract class trong Java

package net.vncoding;

abstract class Drawing {

    protected int x = 0;
    protected int y = 0;

    public abstract double area();

    public String getCoordinates() {
        
        return String.format("x: %d, y: %d", this.x, this.y);
    }
}

class Circle extends Drawing {

    private int r;

    public Circle(int x, int y, int r) {
        
        this.x = x;
        this.y = y;
        this.r = r;
    }

    @Override
    public double area() {
        
        return this.r * this.r * Math.PI;
    }

    @Override
    public String toString() {
        
        return String.format("Circle at x: %d, y: %d, radius: %d",
                this.x, this.y, this.r);
    }
}

public class AbstractClass {

    public static void main(String[] args) {
        
        Circle c = new Circle(12, 45, 22);
        
        System.out.println(c);
        System.out.format("Area of circle: %f%n", c.area());
        System.out.println(c.getCoordinates());
    }
}

Giải thích:
Chúng ta có abstract class ‘Drawing’. Class này định nghĩa 2 member, định nghĩa 1 phương thức và khai báo 1 phương thức. Một phương thức chưa được implement là abstract, phương thức còn lại được implement đầy đủ body. Class ‘Drawing’ là abstract vì có 1 abstract method.

abstract class Drawing {

Sử dụng từ khóa abstract để khai báo abstract class.

public abstract double area();

Để khai báo abstract method, sử dụng từ khóa abstract. Phương thức area() chỉ được khai báo, chưa được implement trong abstract class ‘Drawing’ vì class ‘Drawing’ không mô tả chi tiết là hình chữ nhật, tròn, tam giác,…. Do vậy, chúng ta cũng không thể implement phương thức area().

class Circle extends Drawing {

Class ‘Circle’ là subclass của class ‘Drawing’. Do đó, nó phải implement phương thức area()

@Override
public double area() {
    
    return this.r * this.r * Math.PI;
}

Phương thức area() được implement trong subclass ‘Circle’.

Kết quả:
Circle at x: 12, y: 45, radius: 22
Area of circle: 1520.530844
x: 12, y: 45

Chúng ta tạo object ‘Circle’ và hiển thị diện tích và tọa độ tâm (x, y)

Interface

Điều khiển từ xa là 1 interface giữa user và TV. Nó là 1 interface với thiết bị điện tử. Luật giao thông là quy tắc mà người điều kiển xe máy, ô tô, xe đạp,… phải tuân theo. Vậy khái niệm interface trong lập trình cũng tương tự như các ví dụ ở trên.

Interfaces là:
– APIs
– Contracts

Các object tương tác với thế giới bên ngoài thông qua các phương thức. Đôi khi việc implement thực tế không quan trọng với lập trình viên, hoặc nó là 1 bí mật. 1 công ty có thể bán thư viện và không muốn công khai các body của phương thức được code thế nào. Lập trình viên có thể gọi phương thức maximize() trên cửa sổ của GUI toolkit mà không biết phương thức maximize() được implement như thế nào. Từ cách nhìn này, interface là cách mà object tương tác với thế giới bên ngoài, mà không nói rõ về hoạt động bên trong.

Từ cách nhìn thứ 2 này, interface là hợp đồng. Nếu đạt được thỏa thuận, họ phải tuân theo. Chúng được sử dụng để design kiến trúc của application, giúp tổ chức cấu trúc source code.

Interface là kiểu abstract. Chúng được khai báo sử dụng từ khóa interface. Trong Java, interface là loại tham chiếu, tương tự như class chỉ có thể hằng số, signature của phương thức,.. Không có body của phương thức. Interface không có object, mà chỉ có thể được implement bởi class hoặc được mở rộng bởi các interface khác.
Tất cả các member của interface có quyền truy cập public. Giao diện không thể có các phương thức đã được implement. Class Java có thể implement interface với số lượng bất kì. Interface cũng được mở rộng với số lượng bất kì. Class implement interface phải implement tất cả các phương thức của interface.

Interface được sử dụng để mô phỏng đa kế thừa. Class có thể kế thừa từ 1 class nhưng nó có thể implement nhiều interface. Đa kế thừa với interface là không kế thừa phương thức và biến. Nó chỉ kế thừa về ý tưởng và hợp đồng được mô tả bởi interface.

Body của interface bao gồm các abstract methods, nhưng vì tất cả các phương thức trong interface là abstract, từ khóa abstract là không bắt buộc. Vì interface chỉ ra một số hành vi, tất cả các phương thức được hiểu ngầm định là public. Ngoài việc khai báo phương thức, Interface có thể bao gồm khai báo hằng số. Tất cả các hằng số được định nghĩa trong interface là public, static và final.

Một khác nhau quan trọng giữa interface và abstract class. Abstract class cung cấp 1 số phương thức đã được implement cho class mà liên quan đến việc kế thừa phân cấp. Còn interface có thể được implement bởi class và không liên quan với nhau. Ví dụ, chúng ta có 2 class button. 1 classic button và 1 là round button. Cả 2 đều kế thừa từ abstract class button (cung cấp các function chung cho tất cả các button). Việc implement class là có liên quan, vì tất cả là button. Ngược lại, class ‘Database’ và ‘SignIn’ không liên quan với nhau. Chúng ta có thể sử dụng inteface ‘ILoggable’ để ép chúng tạo phương thức đăng nhập.

Ví dụ 2: Minh họa sử dụng interface trong Java

package net.vncoding;

interface IInfo {

    void doInform();
}

class Some implements IInfo {

    @Override
    public void doInform() {
        
        System.out.println("This is Some Class");
    }
}

public class SimpleInterface {

    public static void main(String[] args) {

        Some sm = new Some();
        sm.doInform();
    }
}

Giải thích:
Đây là ví dụ đơn giản minh họa sử dụng interface trong Java.

interface IInfo {

    void doInform();
}

Đây là interface IInfo, có khai báo phương thức doInform().

class Some implements IInfo {

Chúng ta implement interface IInfo. Dể implement interface IInfo chúng ta sử dụng từ khóa implements

@Override
public void doInform() {
    
    System.out.println("This is Some Class");
}

Class ‘Some’ thực hiện implement phương thức doInform(). Từ khóa @Override nói cho compiler rằng chúng ta đang override phương thức.
Java không cho phép kế thừa trực tiếp từ nhiều hơn 1 class. Nó cho phép implement nhiều interface. Trong ví dụ tiếp theo minh họa class có thể implment nhiều interface.

Ví dụ 3:Minh họa việc class implement nhiều interface.

package net.vncoding;

interface Device {

    void switchOn();

    void switchOff();
}

interface Volume {

    void volumeUp();

    void volumeDown();
}

interface Pluggable {

    void plugIn();

    void plugOff();
}

class CellPhone implements Device, Volume, Pluggable {

    @Override
    public void switchOn() {
        
        System.out.println("Switching on");
    }

    @Override
    public void switchOff() {
        
        System.out.println("Switching on");
    }

    @Override
    public void volumeUp() {
        
        System.out.println("Volume up");
    }

    @Override
    public void volumeDown() {
        
        System.out.println("Volume down");
    }

    @Override
    public void plugIn() {
        
        System.out.println("Plugging in");
    }

    @Override
    public void plugOff() {
        
        System.out.println("Plugging off");
    }
}

public class MultipleInterfaces {

    public static void main(String[] args) {

        CellPhone cp = new CellPhone();
        cp.switchOn();
        cp.volumeUp();
        cp.plugIn();
    }
}

Giải thích:
Class ‘CellPhone’ kế thừa từ nhiều interface

class CellPhone implements Device, Volume, Pluggable {

Class implement 3 interface được phân tách bởi dâu phẩy. Class CellPhone phải implement tất cả các phương thức chưa được implment trong 3 interface.

Kết quả:
Switching on
Volume up
Plugging in

Ví dụ 4: Minh họa việc interface có thể kế thừa từ interface khác.

package net.vncoding;

interface IInfo {

    void doInform();
}

interface IVersion {

    void getVersion();
}

interface ILog extends IInfo, IVersion {

    void doLog();
}

class DBConnect implements ILog {

    @Override
    public void doInform() {
        
        System.out.println("This is DBConnect class");
    }

    @Override
    public void getVersion() {
        
        System.out.println("Version 1.02");
    }

    @Override
    public void doLog() {
        
        System.out.println("Logging");
    }

    public void connect() {
        
        System.out.println("Connecting to the database");
    }
}

public class InterfaceHierarchy {

    public static void main(String[] args) {

        DBConnect db = new DBConnect();
        db.doInform();
        db.getVersion();
        db.doLog();
        db.connect();
    }
}

Giải thích:
Trong ví dụ, chúng ta định nghĩa 3 interface. Các interface được tổ chức dạng phân cấp.

interface ILog extends IInfo, IVersion {

Inteface ‘ILog’ kế thừa từ 2 interface còn lại

class DBConnect implements ILog {

Class ‘DBConnet’ implement interface ‘ILog’. Do đó, nó phải implement tất cả các phương thức của 3 interface.

@Override
public void doInform() {
    
    System.out.println("This is DBConnect class");
}

Class ‘DBConnect’ implement phương thức doInform(). Phương thức này được kế thừa bởi interface ILog.

Kết quả:
This is DBConnect class
Version 1.02
Logging
Connecting to the database

Đa hình trong Java

Đa hình là quy trình sử dụng toán tử hoặc hàm theo cách khác nhau để cho dữ liệu đầu vào khác nhau. Trong thực tế, đa hình có nghĩa là nếu class ‘B’ kế thừa từ class ‘A, nó sẽ không phải kế thừa mọi cái từ class ‘A’ mà nó có thể thêm một vài function khác với class ‘A’.

Ví dụ 5: Minh họa đa hình trong Java

package net.vncoding;

abstract class Shape {

    protected int x;
    protected int y;

    public abstract int area();
}

class Rectangle extends Shape {

    public Rectangle(int x, int y) {
        
        this.x = x;
        this.y = y;
    }

    @Override
    public int area() {
        
        return this.x * this.y;
    }
}

class Square extends Shape {

    public Square(int x) {
        
        this.x = x;
    }

    @Override
    public int area() {

        return this.x * this.x;
    }
}

public class Polymorphism {

    public static void main(String[] args) {

        Shape[] shapes = { new Square(5),
            new Rectangle(9, 4), new Square(12) };

        for (Shape shape : shapes) {
            
            System.out.println(shape.area());
        }
    }
}

Giải thích:
Trong ví dụ này, chúng ta có 1 abstract class ‘Shape’. 2 class ‘Rectangle’ và ‘Square’ kế thừa từ abstract class.
Cả 2 class này implement phương thức area() để tính diện tích cho mỗi chữ nhật và hình vuông. Đa hình mang tới tính linh hoạt và khả năng mở rộng cho các hệ thống OOP.

@Override
public int area() {
    
    return this.x * this.y;
}
...
@Override
public int area() {

    return this.x * this.x;
}

Class ‘Rectangle’ và ‘Square’ implement phương thức area()

Shape[] shapes = { new Square(5),
    new Rectangle(9, 4), new Square(12) };

Chúng ta tạo mảng 3 đối tượng hình.

for (Shape shape : shapes) {
    
    System.out.println(shape.area());
}

Chúng ta duyệt qua mỗi hình vẽ và gọi phương thức area(). Compiler gọi đúng các phương thức của mỗi object. Đây chính là bản chất của đa hình.

Kết quả:
25
36
144

Nested class trong Java

Có thể định nghĩa 1 class bên trong 1 class khác, class này được gọi là nested class. Class không được lồng nhau gọi là top-level class.
Java có 4 loại nested class.
– Static nested classes
– Inner classes
– Local classes
– Anonymous classes

Sử dụng nested class có thể tăng tính dễ đọc source code và cải thiện cấu trúc của source code. Inner class thường được dùng như callback trong GUI. Ví dụ trong Swing toolkit.

Static nested class

Static nested class là nested class được tạo với không cần instance của class “bao quanh”. Nó có quyền truy cập tới biến static và phương thức của lớp “bao quanh”

Ví dụ 6:Minh họa việc sử dụng static nested class

package net.vncoding;

public class SNCTest {
    
    private static int x = 5;
    
    static class Nested {
                       
        @Override
        public String toString() {
            return "This is a static nested class; x:" + x;
        }
    }
    
    public static void main(String[] args) {
    
        SNCTest.Nested sn = new SNCTest.Nested();
        System.out.println(sn);
    }
}

Giải thích:
Đây là ví dụ mô tả static nested class.

private static int x = 5;

Đây là biến static private của SNCTest class. Static nested class có thể truy cập tới biến ‘x’

static class Nested {
                    
    @Override
    public String toString() {
        return "This is a static nested class; x:" + x;
    }
}

Định nghĩa static nested class. Override phương thức toString() để in ra message và tham chiếu tới biến static ‘x’

SNCTest.Nested sn = new SNCTest.Nested();

Toán tử chấm (.) được sử dụng để tham chiếu đến nested class.

Kết quả:
This is a static nested class; x:5

Inner class

Instance của class thông thường hoặc top-level có thể tồn tại 1 mình. Nhưng ngược lại, instance của inner class không thể được thể hiện mà không bị ràng buộc với class bao quanh. Inner class cũng coi như 1 member của 1 class bao quanh. Chúng thuộc về instance của lớp bao quanh. Inner class có quyền truy cập tới member của class bao quanh.

Ví dụ 7: Minh họa việc sử dụng inner class

package net.vncoding;

public class InnerClassTest {
    
    private int x = 5;
    
    class Inner {
        
        @Override
        public String toString() {
            return "This is Inner class; x:" + x;
        }
    }

    public static void main(String[] args) {
    
        InnerClassTest nc = new InnerClassTest();
        InnerClassTest.Inner inner = nc.new Inner();
        
        System.out.println(inner);
    }
}

Giải thích:
Nested class được định nghĩa bên trong class ‘InnerClassTest’. Nó có quyền truy xuất đến biến ‘x’

class Inner {
    
    @Override
    public String toString() {
        return "This is Inner class; x:" + x;
    }
}

Inner class được định nghĩa bên trong body của class ‘InnerClassTest’

InnerClassTest nc = new InnerClassTest();

Trước hết, chúng ta cần tạo instance của class cấp cao hơn. Inner class không thể tồn tại mà không có instance của class ‘InnerClassTest’

InnerClassTest.Inner inner = nc.new Inner();

Chúng ta có thể tạo instance của inner class thông qua instance của class ‘InnerClassTest’

Kết quả:
This is Inner class; x:5

Nếu biến có phạm vi inner mà có cùng tên với biến có phạm vi bên ngoài. Nó có thể tham chiếu tới biến có phạm vi bên ngoài. Các bạn xem tiếp ví dụ sau.

Ví dụ 8: Minh họa biến cùng tên có phạm vi khác trong inner class.

package net.vncoding;

public class Shadowing {
    
    private int x = 0;
    
    class Inner {
        
        private int x = 5;
        
        void method1(int x) {
            
            System.out.println(x);
            System.out.println(this.x);
            System.out.println(Shadowing.this.x);            
        }
    }

    public static void main(String[] args) {
        
        Shadowing sh = new Shadowing();
        Shadowing.Inner si = sh.new Inner();
        
        si.method1(10);    
    }
}

Giải thích:
Định nghĩa biến ‘x’ trong class ‘Shadowing’, ‘Inner’ và trong tham số phương thức

System.out.println(x);

Dòng code này tham chiếu tới biến ‘x’ được định nghĩa với phạm vi phương thức.

System.out.println(this.x);

Sử dụng từ khóa this, chúng ta tham chiếu tới biến ‘x’ được định nghĩa trong class ‘Inner’

System.out.println(Shadowing.this.x);

Chúng ta tham chiếu tới biến ‘x’ của class ‘Shadowing’

Kết quả:
10
5
0

Local class

Local class là 1 trường hợp đặc biệt của inner class. Local class là class được định nghĩa trong block (block là 1 nhóm các lệnh được bao quanh bởi dấu {} )
Ngoài ra, local class có quyền truy cập tới biến local nếu chúng được khai báo với từ khóa final. Lý do cho vấn đề này là kĩ thuât. Thời gian sống của instance của local class có thể dài hơn rất nhiều so với thời gian thực thi của phương thức trong class được định nghĩa. Để giải quyết vấn đề này, biến local được copy vào local class. Để đảm bảo rằng sau đó chúng không bị thay đổi, phải sử dụng từ khóa final.

Local class không thể là kiểu public, private, protected hoặc static. Chúng không được phép khai báo biến local hoặc khai báo local class. Ngoại trừ các hằng số được khai báo là static và final, local class không thể bao gồm member, phương thức hoặc class kiểu static.

Ví dụ 8: Minh họa việc sử dụng local class

package net.vncoding;

public class LocalClassTest {

    public static void main(String[] args) {
    
        final int x = 5;       
        
        class Local {
            
            @Override
            public String toString() {
                return "This is Local class; x:" + x;
            }
        }
        
        Local loc = new Local();
        System.out.println(loc);        
    }
}

Giải thích:
Local class được định nghĩa bên trong hàm main().

@Override
public String toString() {
    return "This is Local class; x:" + x;
}

local class có thể truy cập tới biến local nếu các biến được khai báo với từ khóa final.
A local class can access local variables if they are declared final.

Kết quả:
This is Local class; x:5

Anonymous class

Anonymous class là local class mà không có tên. Chúng cho phép chúng ta khai báo và tạo object class tại cùng 1 thời điểm. Chúng ta có thể sử dụng anonymous class nếu chúng ta muốn sử dụng class chỉ 1 lần. anonymous class được định nghĩa và tạo object trong 1 biểu thức. Anonymous inner classes cũng được sử dụng trong trường hợp xử lí sự kiện được sử dụng bởi 1 component và do đó không tên tham chiếu.

Anonymous class phải implement interface hoặc kế thừa class. Nhưng từ khóa implementsextends không được sử dụng. Nếu tên theo sau từ khóa new là tên của class, anonymous class là subclass. Nếu tên theo sau từ khóa new chỉ định interface, anonymous class implement interface và mở rộng Object.

Vì anonymous class không có tên, không thể định nghĩa hàm tạo cho anonymous class. Bên trong body của anonymous class, chúng ta không thể định nghĩa các câu lệnh một cách tùy ý; chỉ các phương thức hoặc member.

Ví dụ 9: Minh họa sử dụng anonymous class trong Java

package net.vncoding;

public class AnonymousClass {
    
	   interface Message {
	        public void send();
	    }    

	    public void createMessage() {

	        Message msg = new Message() {
	            
	            @Override
	            public void send() {
	                System.out.println("This is a message");
	            }
	        };

	        msg.send();
	    }

	    public static void main(String[] args) {
	        
	        AnonymousClass ac = new AnonymousClass();
	        ac.createMessage();
	    }
	}

Giải thích:

interface Message {
    public void send();
}  

Anonymous class phải được subclass hoặc phải implement 1 interface. Trong ví dụ này, anonymous class implement interface ‘Message’.

public void createMessage() {

    Message msg = new Message() {
        
        @Override
        public void send() {
            System.out.println("This is a message");
        }
    };

    msg.send();
}

Anonymous class là local class, do vậy nó được định nghĩa trong body của phương thức. Anonymous class được định nghĩa trong 1 biểu thức. Do đó, phải có dấu chấm phẩy (;) đi sau dấu }

Kết quả:
This is a message

Be the first to comment

Leave a Reply