Lớp (class) và đối tượng (object)

Java là ngôn ngữ lập trình hướng đối tượng (object oriented programming). Thành phần cơ bản nhất của các ngôn ngữ hướng đối tượng nói chung và Java nói riêng là lớp và đối tượng.

Lớp (class)

Lớp là một mô tả tổng quát về một kiểu đối tượng nào đó – những gì đối tượng có (các đặc trưng hay thuộc tính của đối tượng) và đối tượng có thể làm được gì (các chức năng của đối tương). Mô tả trong Java, các đặc trưng hay thuộc tính là các biến thành viên của lớp, các tính năng là các hàm hay phương thức thành viên của lớp.

Ví dụ lớp Circle sở hữu thành phần quan trọng là radius và các phương thức computePerimiter – tính chu vi và phương thức computeArea – tính diện tích.

Tạo một lớp trong Eclipse, ví dụ lớp Circle, tương tự cách tạo lớp HelloWorld trong chương III. Lưu ý rằng:

  • Lớp phải được chứa trong một gói (package) nào đó
  • Tên lớp trùng với tên tập tin Java, ví dụ java

Cú pháp của mộp lớp, ví dụ lớp Circle như sau:


public class Circle {

}

Để tiện theo dõi, chúng ta tạo thêm một lớp tên là MyMainClass chứa phương thức main. Tập tin MyMainClass.java có nội dung như sau:


public class MyMainClass {

public static void main(String[] args)  {

// TODO Auto-generated method stub

}

}

Lớp CircleMyMainClass thuộc về cùng một package (default package):

Biến thành viên (member variables hay fields)

Biến thành viên của một lớp được khai báo bên trong lớp và không thuộc bất cứ phương thức nào của lớp. Ví dụ khai báo các biến thành viên cho lớp Circle như sau:


public class Circle {

public float radius;

private String name;

protected float lineWidth;

private static int circleCount;

}

Như khai báo trên, lớp Circle có 4 biến thành viên là radius, name, linewidth, và circleCount. Lưu ý rằng, bên cạnh kiểu dữ liệu, mỗi biến còn đi kèm với một từ khóa chỉ phạm vi truy cập là:

  • public: các biến hay phương thức của lớp được đánh dấu là public có thể được truy cập (hay sử dụng) bởi tất cả các lớp.
  • private: các biến (hay các phương thức nhưng trường hợp này ít khi xảy ra) được đánh dấu là private chỉ có thể được truy cập bởi chính lớp chứa biến đó.
  • protected: các biến (hay các phương thức nhưng trường hợp này ít khi xảy ra) được đánh dấu là protected chỉ có thể được truy cập bởi các lớp hay đối tượng trong cùng một gói (package) với lớp chứa biến đó.
  • static: nếu một biến thành viên được đánh dấu là static, chỉ có một bản sao của biến được chia sẻ cho tất cả các đối tượng của lớp đó. Các thành viên (biến hay phương thức) static được tham chiếu đến lớp thay vì đối tượng.

Đối tượng (object)

Nếu lớp là một mô tả tổng quát cho một kiểu đối tượng nào đó thì đối tượng là các thể hiện (instances) cụ thể của lớp đó. Ví dụ các đối tượng của lớp Circle sẽ là những Circle có các radius khác nhau.

Để tạo một đối tượng từ một lớp, chúng ta dùng từ khóa new. Ví dụ sau sẽ tạo hai đối tượng c1c2 của lớp Circle trong phương thức main của lớp MyMainClass:


public class MyMainClass {


public static void main(String[] args)  {

// TODO Auto-generated method stub

Circle c1 = new Circle();

Circle c2 = new Circle();

}

}

Để ý sau từ khóa newCircle(), đây là phương thức khởi tạo mặc định của lớp Circle. Phương thức khởi tạo sẽ được đề cập kĩ hơn trong phần phương thức.

Chúng ta có thể truy cập đến các biến thành viên của lớp thông qua đối tượng bằng dấu chấm. Ví dụ truy cập và gán các giá trị đến biến radius của đối tượng c1c2 như sau:


c1.radius = 15.0f;

c2.radius = 30.0f;

radius có phạm vi truy cập là public nên chúng ta có thể truy cập từ lớp MyMainClass. Bây giờ chúng ta thử truy cập đến các biến thành viên còn lại:


c1.lineWidth = 3.5f;// ok

c2.lineWidth = 5.6f; // ok

c1.name = "firstobj"; // lỗi

c2.name = "secondobj";// lỗi

Có thể truy cập đến lineWith vì biến này có phạm vi là protected nên có thể được truy cập từ lớp MyMainClass – lớp cùng gói với lớp Circle.

Truy cập đến biến name sẽ bị lỗi vì phạm vi truy cập của biến này là private.

Phương thức (mehod)

Bên cạnh các biến thành viên, một thành phần quan trọng khác của một lớp là phương thức hay các hàm. Phương thức gồm hai kiểu: kiểu có giá trị trả về và kiểu không có giá trị trả về. Giống như biến, phương thức cũng có các phạm vi truy cập như public, private, hay protected. Phương thức cũng có thể là static.

Phương thức có giá trị trả về có cú pháp như sau:

[public|private|protected] [static] Datatype method_name (params list)

{

...

return...;

}

Cần chú ý:

  • Phương thức có kiểu dữ liệu trả về (Datatype) như kiểu int, float, double, v.v.
  • Lệnh kết thúc trong phần thân phương thức là lệnh return. Theo sau return có thể là một giá trị, một biến, hay một biểu thức và tất cả phải có kiểu dữ liệu là Datatype – cùng kiểu với khai báo phương thức.
  • params list là danh sách chứa các tham số. Danh sách có thể rỗng hay chứa nhiếu tham số. Nếu chứa nhiều tham số thì các tham số cách nhau bởi dấu phẩy.

Ví dụ phương thức sau:


public int add (int a, int b) {

return (a+b);

}

Phương thức trên có phạm vi truy cập là public; kiểu trả về là int; hai tham số trong danh sách tham số là ab. Giá trị trả về của biểu thức (a+b) có kiểu int.

Nếu viết như sau trình biên dịch Java sẽ phát sinh lỗi:


public int add (double a, double b) {

return (a+b);

}

ab có kiểu dữ liệu là double nên (a+b) sẽ trả về giá trị có kiểu double khác kiểu int nên sẽ phát sinh lỗi. Cần thận trọng khi khai báo phương thức có giá trị trả về.

Phương thức không có giá trị trả về có cú pháp sau:

[public|private|protected] [static] void method_name (params list)

{

...

}

Có hai điểm khác biệt so với phương thức có giá trị trả về là kiểu dữ liệu được thay bằng từ khóa void và không có lệnh return tại cuối phần thân phương thức. Ví dụ phương thức add có thể được viết lại như sau:


public void add (double a, double b) {

double c = a + b;

System.out.println(c);

}

Phương thức khởi tạo (constructor)

Phương thức mà mọi lớp đều có là phương thức khởi tạo (constructor). Phương thức khởi tạo là một phương thức đặc biệt:

  • Tên phương thức trùng với tên lớp
  • Không có kiểu trả về hay từ khóa void
  • Phương thức khởi tạo mặc định là phương thức khởi tạo không có tham số trong danh sách tham số.

Để dễ hình dung, chúng ta sẽ khai báo các phương thức và phương thức khởi tạo cho lớp Circle như sau:


public class Circle {

private float radius;

private String name;

private float lineWidth;

private static int circleCount;


// phương thức khởi tạo mặc định

public Circle() {

this.radius = 0.0f;

this.name = "No name";

this.lineWidth = 0.0f;

circleCount++;

}

// Phương thức khởi tạo có tham số

public Circle(String name, float radius) {

this.radius = radius;

this.name = name;

this.lineWidth = 0.0f;

circleCount++;

}

// các phương thức

public void setRadius(float radius) {

this.radius = radius;

}

public float getRadius() {

return radius;

}

public void print(){

System.out.println("Circle: " + name + " Rad: " + radius);

}

protected void setLineWidth(float newWidth) {

this.lineWidth = newWidth;

}

public static void zeroCount() {

circleCount = 0;

}

public static int getCount() {

return circleCount;

}

}

Có một vài điểm quan trọng cần chú ý ở đây:

  • Các biến thành viên trong một lớp nên có phạm vi truy cập là private (hay có thể là protected) vì đây là thành phần quan trọng của lớp và chúng ta không muốn lớp khác truy cập trực tiếp. Các biến thành viên trong Java hay còn được gọi là các fields (tương đương với fields trong C#).
  • Vì các biến thành viên có phạm vi là private nên các lớp khác muốn truy cập các biến này phải thông qua các phương thức. Các phương thức này được gọi là các setter hay getter trong C#, property trong Visual Basic.Net. Ví dụ để gán giá trị đến biến radius chúng ta dùng phương thức setRadius, để lấy giá trị của biến radius chúng ta dùng phương thức getRadius.
  • Các tham số trong phương thức còn được gọi là các biến cục bộ (local variables). Các biến này chỉ có thể được sử dụng trong phạm vi phương thức. Biến cục bộ có thể trùng tên và kiểu với tên biến thành viên và phương thức sẽ ưu tiên sử dụng biến cục bộ. Do đó, trong một phương thức có biến cục bộ trùng tên và kiểu với biến thành viên, để phân biệt, chúng ta dùng từ khóa this. this dùng để chỉ đối tượng hiện tại tham chiếu đến biến thành viên và truy cập đến biến thành viên bằng dấu chấm. Ví dụ phương thức setRadius có biến cục bộ là radius (trong danh sách tham số) và giá trị của biến này được gán đến biến thành viên radius thông qua từ khóa this:

this.radius = radius;

  • Trong một lớp có thể có nhiều phương thức khởi tạo. Các phương thức khởi tạo có thể được định nghĩa khác nhau bởi số tham số trong danh sách tham số hay có cùng số tham số nhưng có kiểu dữ liệu khác nhau. Quá trình này gọi là quá tải phương thức khởi tạo (constructor overloading). Trong lớp Circle có hai phương thức khởi tạo, một phương thức khởi tạo mặc định không có tham số và một phương thức khởi tạo chứa hai tham số là nameradius. Quá tải cũng có thể áp dụng cho các phương thức nói chung. Ví dụ chúng ta có thể tạo hai phương thức setRadius như sau:

public void setRadius(float radius) {

...

}

public void setRadius(int radius) {

...

}

  • Biến static chỉ có thể được truy cập bởi các phương thức static và có thể truy cập trực tiếp thay vì dùng this. Ví dụ biến circleCount được sử dụng trong các phương thức staticzeroCount hay getCount.

Gọi phương thức

Các phương thức có thể được truy cập thông qua các đối tượng hay lớp (phương thức static) như đoạn mã sau:


public class MyMainClass {

public static void main(String[] args)  {

// TODO Auto-generated method stub

// tạo đối tượng c dùng constructor mặc định

Circle c = new Circle();

// tạo đối tượng b dùng constructor có hai tham số

Circle b = new Circle("Dennis", 55.0f);

// gọi phương thức static getCount

System.out.println("Number of circles created: " +

Circle.getCount());

b.print(); // gọi phương thức print của b

c.setRadius(27.0f);// gọi setRadius để gán c.radius đến 27.0f.

c.setLineWidth(100.6f); // gán c.lineWidth đến 100.6f.

System.out.println("Radius of C: " + c.getRadius());

Circle.zeroCount(); // gọi phương thức static zeroCount

System.out.println(

"Number of circles created: " + Circle.getCount());

}

}

Các thành phần static

Thành viên static

Các phương thức hay biến thành viên của một lớp có phạm vi truy cập là public và có kiểu static có thể được truy cập thông qua các thể hiện (đối tượng) hay tên lớp.

Ví dụ các phương thức getCount() hay zeroCount() là các phương thức static. Các phương thức này có thể được truy cập bằng tên lớp:


Circle.getCount();

hay thông qua một thể hiện:


c.getCount();

Biến circleCount là biến static của lớp Circle. Mỗi biến static được Java cấp phát bộ nhớ chỉ một lần và tất cả các đối tượng của một lớp sẽ tham chiếu đến cùng một không gian bộ nhớ của biến đó. Biến static tồn tại ngay cả khi không có đối tượng nào được khởi tạo và luôn được khởi tạo đến một giá trị mặc định.

Lớp static

Java cho phép chúng ta định nghĩa các lớp lồng nhau – tức là định nghĩa một lớp (inner class) trong một lớp khác (outer class). Một lớp được định nghĩa bên trong một lớp khác có thể là lớp static (static inner class). Như vậy, khi chúng ta đề cập đến một lớp static tức là chúng ta đang đề cập đến một lớp được định nghĩa bên trong một lớp khác.

Một lớp được định nghĩa bên trong một lớp khác có thể là lớp static (static inner class) hay một lớp bình thường (non-static inner class). Hai kiểu lớp này có một số điểm khác biệt:

  • non-static inner class yêu cầu một tham chiếu của outer class trong khi static inner class không cần.
  • non-static inner class có thể truy cập đến các thành viên (biến hay phương thức) static và không static của outer class. static inner class chỉ có thể truy cập đến các thành viên static của outer class.
  • Thể hiện của non-static inner class được tạo ra thông qua thể hiện của outer class.

Một ví dụ về các lớp lồng nhau:


class OuterClass{

private static String msg = "ngocminhtran";

public static class StaticInnerClass{

public void printMessage() {

System.out.println("Message from nested static class: " + msg);

}

}

public class NonStaticInnerClass{

public void display(){

System.out.println("Message from non-static nested class: "+ msg);

}

}

}

msg là biến thành viên của lớp OuterClass. Lớp StaticInnerClass có thể truy cập được msgmsg là thành viên static. Lớp NonStaticInnerClass có thể truy cập được msg vì lớp này có thể truy cập được mọi thành viên static và non-static của lớp OuterClass. Các lớp này có thể được sử dụng như sau:


// tạo thể hiện của lớp StaticInnerClass

OuterClass.StaticInnerClass printer = new OuterClass. StaticInnerClass ();

printer.printMessage();

//tạo thể hiện của lớp NonStaticInnerClass thông qua thể hiện của OuterClass

// bằng hai dòng mã

OuterClass outer = new OuterClass();

OuterClass.NonStaticInnerClass inner  = outer.new NonStaticInnerClass();

inner.display();

//tạo thể hiện của lớp NonStaticInnerClass thông qua thể hiện của OuterClass

// chỉ bằng một dòng mã

OuterClass.NonStaticInnerClass innerObject = new OuterClass().new NonStaticInnerClass();

innerObject.display();

Lược đồ UML (Unified Model Language)

Một lớp có thể được biểu diễn bằng lược đồ UML. Một lược đồ UML biểu diễn cho một lớp có thể được chia thành 3 phần như sau:

Ví dụ lớp Circle có thể được biểu diễn bằng lược đồ UML như sau:

Màu sắc của mỗi phần là tùy ý. Tên lớp ở giữa phần đầu tiên, các biến bắt đầu bằng dấu “-”, các phương thức bắt đầu bằng dấu “+”.

Thừa kế (Inheritance)

Thừa kế là một trong những đặc điểm quan trọng nhất của lập trình hướng đối tượng. Thừa kế giúp chúng ta tiết kiệm thời gian, viết ít mã, và tránh lặp lại các đoạn mã. Thừa kế tạo ra một cấu trúc các lớp dựa theo mối quan hệ cha/con (parent/child) bằng cách kế thừa lại các đặc điểm từ các lớp (lớp cha) có sẵn và mở rộng thêm các đặc điểm trong các lớp mới (lớp con). Ví dụ chúng ta có lớp Animal như sau:


public class Animal {

public boolean voluntaryMotion;

public boolean requiresFood;

public Animal(){

voluntaryMotion = true;

requiresFood = true;

}

}

Lớp Insect thừa kế các thành viên có phạm vi truy cập là public hay protected từ lớp Animal và bổ sung thêm một số thành viên mới như sau:


public class Insect extends Animal {

public boolean antennas;

public String skeleton;

public int numberOfLegs;

public boolean wings;

public Insect(){

super();

antennas = true;

skeleton = "Exosketon";

numberOfLegs = 6;

wings = true;

}

public void Print() {

System.out.println("Antennas: " + antennas +

" Skeleton: " + skeleton +

" Number of Legs: " + numberOfLegs +

" Wings: " + wings +

" Voluntary Motion: " + voluntaryMotion +

" Requires Food: " + requiresFood);

}

}

Có một số chú ý trong định nghĩa lớp Insect:

  • Lớp Insect thừa kế từ lớp Animal bằng cách dùng từ khóa extends
  • Phương thức super() gọi phương thức constructor mặc định (không có tham số) từ lớp cha (Animal). super(), nếu được gọi, phải là dòng đầu tiên trong phương thức constructor của lớp con.
  • Insect chỉ kế thừa các thành phần public hay protected từ lớp Animal.

Chúng ta có thể biểu diễn hai lớp có mối quan hệ cha/con bằng lược đồ UML sử dụng ký hiệu mũi tên:

Sử dụng lớp Insect:


public class MyMainClass {

public static void main(String[] args)  {

// TODO Auto-generated method stub

// Create an instance of Insect called i.

Insect i = new Insect();

// Change the insect class's members:

i.antennas = false;

i.numberOfLegs = 100;

// Call the Insect's Print method:

i.Print();

// Change the parent class's requiresFood member:

i.requiresFood = false;

}

}

Đa hình (Polymorphism)

Bên cạnh tính năng thừa kế, đa hình là một trong những tính năng quan trọng nhất của lập trình hướng đối tượng. Với khả năng đa hình, các lớp con (child classes) có thể sử dụng lại các phương thức được thừa kế từ lớp cha (parent classes) theo những cách khác nhau. Chúng ta có thể sử dụng tính năng đa hình thông qua các lớp trừu tượng (abstract classes) hay các giao diện (interfaces).

Các lớp trừu tượng (abstract classes)

Giả sử chúng ta có lớp Person với phương thức thành viên là Talk(). Hai lớp StudentEmployee thừa kế từ lớp Person với phương thức Talk() được định nghĩa lại phù hợp với từng đối tượng Student hay Employee. Để thực hiện được điều này, chúng ta có thể khai báo lớp cha Person là một lớp trừu tượng bằng cách dùng từ khóa abstract:


//Abstract parent class:

public abstract class Person {

//Abstract method:

public abstract void Talk();

}

Lưu ý rằng, từ khóa abstract được sử dụng trong khai báo lớp Person và khai báo phương thức Talk(). Không thể tạo thể hiện (hay đối tượng) từ một lớp trừu tượng. Các phương thức trong một lớp cha trừu tượng có thể được sử dụng lại trong các lớp con theo hai cách: phương thức trừu tượng và ghi đè phương thức.

Phương thức trừu tượng (abstract methods)

Phương thức Talk() được gọi là phương thức trừu tượng. Một phương thức trừu tượng không có phần thân phương thức.

Lớp Student thừa kế từ lớp Person có thể được định nghĩa như sau:


public class Student extends Person {

public void Talk() {

System.out.println("I am a Student!");

}

}

Lớp Employee:


public class Employee extends Person {

public void Talk() {

System.out.println("I am an Employee!");

}

}

Phương thức Talk() lúc này được định nghĩa lại phù hợp với từng đối tượng. Chúng ta có thể tạo các thể hiện Student hay Employee như sau:


Person student = new Student();

student.Talk();// I am a Student!

Person employee = new Employee();

employee.Talk();// I am an Employee!

Ghi đè phương thức (overriding methods)

Lớp trừu tượng, giống các lớp bình thường khác, cũng có thể chứa các biến hay phương thức thành viên. Lớp Person có thể khai báo lại như sau:


//Abstract parent class:

public abstract class Person {

//Member variables:

String name;

int age;

// Member method

public void Intro() {

System.out.println("My name is " + name + " and I am " + age + " years old.");

}

//Abstract method:

public abstract void Talk();

}

Các lớp StudentEmployee, tất nhiên, cũng sẽ kế thừa các thành viên này:


Person student = new Student();

student.Talk();

student.Intro();// My name is...

Phương thức Intro() của lớp Person có thể được định nghĩa lại phù hợp cho các đối tượng StudentEmployee bằng cách dùng kĩ thuật “ghi đè” phương thức. Đây là kĩ thuật đa hình khác bên cạnh dùng phương thức trừu tượng, ví dụ Talk(). Ghi đè phương thức Intro() trong lớp Student:


public class Student extends Person {

public void Talk() {

System.out.println("I am a Student!");

}

@Override

public void Intro() {

System.out.println("I love reading!");

}

}

Lưu ý rằng, phía trên phương thức Intro() có dùng chú giải @Override. Chú giải này không bắt buộc nhưng nên sử dụng để làm cho đoạn mã trong sáng và dễ đọc hơn.

Lúc này, dùng phương thức Intro() thông qua thể hiện lớp Student:


Person student = new Student();

student.Talk();

student.Intro();// I love reading!

Phương thức khởi tạo (constructors)

Mặc dù chúng ta không được phép tạo các thể hiện (hay đối tượng) từ lớp trừu tượng, nhưng lớp trừu tượng cho phép chúng ta định nghĩa các phương thức khởi tạo. Phương thức khởi tạo mặc định trong lớp Person:


//Abstract parent class:

public abstract class Person {

//Member variables:

String name;

int age;

//Default constructor

public Person() {

name = "Minh";

age = 18;

}

// Member method

public void Intro() {

System.out.println("My name is " + name + " and I am " + age + " years old.");

}

//Abstract method:

public abstract void Talk();

}

Giống như ghi đè các phương thức, các phương thức khởi tạo cũng có thể được định nghĩa lại tại các lớp con. Phương thức khởi tạo của lớp Student:


public class Student extends Person {

//constructor

public Student() {

name = "ngocminhtran";

age = 30;

}

public void Talk() {

System.out.println("I am a Student!");

}


@Override

public void Intro() {

System.out.println("I love reading!");

}

}

Có thể sử dụng phương thức super() trong phương thức khởi tạo của lớp con nếu muốn gọi phương thức khởi tạo từ lớp cha. Chú ý rằng, lệnh gọi super() luôn được đặt đầu tiên trong phương thức khởi tạo của lớp con và có thể gọi theo nhiều phiên bản khác nhau của phương thức khởi tạo. Ví dụ lớp Person có hai phiên bản phương thức khởi tạo như sau:


//Abstract parent class:

public abstract class Person {

//Member variables:

String name;

int age;

//Default constructor

public Person() {

name = "Minh";

age = 18;

}

//Another constructor

public Person(String name, int age) {

this.name = name ;

this.age = age;

}

// Member method

public void Intro() {

System.out.println("My name is " + name + " and I am " + age + " years old.");

}

//Abstract method:

public abstract void Talk();

}

Gọi từ phương thức khởi tạo của lớp Student:


public class Student extends Person {

//constructor

public Student() {

//super();

//super("Minh",18);

}

...

}

Chỉ được gọi một trong hai phiên bản super() vì lệnh super() luôn là lệnh đầu tiên.

Từ khóa instanceof

Từ khóa instanceof hữu ích khi chúng ta cần kiểm tra một thể hiện (hay đối tượng) thuộc kiểu lớp nào. Ví dụ:


Person obj = new Student();

if(obj instanceof Person)

System.out.println("I am a Person.");

else

System.out.println("I am not a Person.");

if(obj instanceof Employee)

System.out.println("I am an Employee.");

else

System.out.println("I am not an Employee.");

Kết quả:


I am a Person.

I am not an Employee.

Giao diện (interface)

Một giao diện tương tự một lớp cha trừu tượng nhưng chỉ chứa các phương thức trừu tượng hay các hằng (constants). Kế từ Java 8, giao diện chứa thêm các phương thức static (static methods) và các phương thức mặc định (default methods) (tìm hiểu thêm tại dzone.com ). Khác với các phương thức trừu tượng, các phương thức static và phương thức mặc định có phần thân phương thức. Phương thức mặc định có thể được ghi đè trong lớp thực thi còn phương thức static thì không thể.

Các lớp thực thi giao diện sẽ thừa kế các thành phần từ giao diện. Trừ khi là một lớp trừu tượng, tất cả các phương thức trong giao diện cần được định nghĩa trong lớp thực thi giao diện đó.

Giao diện được định nghĩa bằng từ khóa interface. Ví dụ giao diện iAnimal như sau:


public interface iAnimal {

public void eat();

public void move();

}

Một lớp sẽ thực thi giao diện với từ khóa implements. Ví dụ lớp MammalInt thực thi giao diện iAnimal như sau:


public class MammalInt implements iAnimal {

public void eat() {

System.out.println("Mammal eats");

}

public void move() {

System.out.println("Mammal moves");

}

}

Giống như lớp, giao diện có thể được mở rộng (hay thừa kế) sang các lớp khác bằng từ khóa extends. Ví dụ:


//Filename: Sports.java

public interface Sports {

public void setHomeTeam(String name);

public void setVisitingTeam(String name);

}

//Filename: Football.java

public interface Football extends Sports {

public void homeTeamScored(int points);

public void visitingTeamScored(int points);

public void endOfQuarter(int quarter);

}

//Filename: Hockey.java

public interface Hockey extends Sports {

public void homeGoalScored();

public void visitingGoalScored();

public void endOfPeriod(int period);

public void overtimePeriod(int ot);

}

Java chỉ cho phép thừa kế đơn với các lớp, nghĩa là một lớp chỉ được thừa kế từ một lớp cha. Tuy nhiên, với giao diện chúng ta có thể thực hiện đa thừa kế như sau:


public interface Hockey extends Sports, Event

Các lớp nặc danh (anonymous classes)

Lớp nặc danh là lớp không có tên và thường được sử dụng khi cần khai báo một lần tại một vị trí nào đó trong đoạn mã. Để dùng lớp nặc danh, chúng ta cần thực thi một giao diện hay mở rộng một lớp có sẵn. Ví dụ sau định nghĩa một lớp nặc danh:


public class MainClass {

// Lớp cha

static class OutputLyrics {

public void output() {

System.out.println("No lyrics supplied...");

}

}

public static void main(String[] args) {

//tạo một thể hiện từ lớp OutputLyrics.

OutputLyrics regularInstance = new OutputLyrics();

// tạo một lớp nặc danh thừa kế từ lớp OutputLyrics

OutputLyrics anonymousClass = new OutputLyrics() {

public void output() {

System.out.println(

"Desmond has a barrow in the market place.");

}

};

// gọi output dùng thể hiện của OutputLyrics

regularInstance.output();

// gọi output dùng thể hiện lớp nặc danh

anonymousClass.output();

}

}

Lớp nặc danh có thể được dùng như một tham số của một phương thức. Ví dụ sau minh họa một lớp nặc danh thực thi một giao diện (MathOperation) đóng vai trò như một tham số của phương thức (performOperation):


public class MainClass {

// interface

interface MathOperation {

public int operation(int a, int b);

}

// Phương thức nhận một đối tượng như một tham số

static int performOperation(int a, int b, MathOperation op) {

return op.operation(a, b);

}

public static void main(String[] args) {

int x = 100;

int y = 97;

// Gọi hàm PerformOperation với phép cộng:

int resultOfAddition = performOperation(x, y,

// lớp nặc danh như là một tham số

new MathOperation() {

public int operation(int a, int b) {

return a + b;

}

});

// Gọi hàm PerformOperation phép trừ:

int resultOfSubtraction = performOperation(x, y,

// lớp nặc danh như là một tham số

new MathOperation() {

public int operation(int a, int b) {

return a - b;

}

});

// Output Addition: 197

System.out.println("Addition: " + resultOfAddition);

// Output Subtraction: 3

System.out.println("Subtraction: " + resultOfSubtraction);

}

}

Ngôn ngữ Java >