Java Bài 36: Lớp Wrapper

Posted by

Chào mừng các bạn đã đến với bài học Java số 36, bài học về lớp Wrapper. Đây là bài học trong chuỗi bài về lập trình ngôn ngữ Java của Yellow Code Books.

Bài học hôm nay đưa chúng ta ngược về quá khứ, để bổ sung thêm kiến thức còn khá “mộc mạc” ở một bài học khi đó. Quá khứ ở đây là lúc mà chúng ta còn đang làm quen với các kiểu dữ liệu nguyên thủy của Java. Lúc bấy giờ bạn đã biết Java có 8 kiểu nguyên thủy, chúng bao gồm int, short, long, byte, float, double, charboolean. Bạn có thể xem lại toàn bộ bài học số 4 để biết rõ thông tin về các kiểu dữ liệu này.

Hôm nay bạn sẽ được làm quen với các lớp có tên là Wrapper. Để xem các lớp này giúp bổ sung gì cho các kiểu dữ liệu nguyên thủy vừa được liệt kê trên đây nhé.

Làm Quen Với Lớp Wrapper

Lớp Wrapper thực chất chỉ là một cái tên chung cho rất nhiều lớp khác nhau. Vì tất cả các lớp thuộc bài hôm nay có cùng một công năng, nên mới gọi chung một cái tên Wrapper như vậy.

Và bởi như mình có nói, Java có 8 kiểu dữ liệu nguyên thủy, nên cũng sẽ có 8 lớp Wrapper cho từng kiểu nguyên thủy này. Chúng bao gồm.

– Lớp Byte là lớp Wrapper cho kiểu dữ liệu byte.
– Lớp Short là lớp Wrapper cho kiểu dữ liệu short.
– Lớp Integer là lớp Wrapper cho kiểu dữ liệu int.
– Lớp Long là lớp Wrapper cho kiểu dữ liệu long.
– Lớp Float là lớp Wrapper cho kiểu dữ liệu float.
– Lớp Double là lớp Wrapper cho kiểu dữ liệu double.
– Lớp Character là lớp Wrapper cho kiểu dữ liệu char.
– Lớp Boolean là lớp Wrapper cho kiểu dữ liệu boolean.

Như vậy bạn có thể thấy, mỗi lớp Wrapper cho mỗi kiểu dữ liệu nguyên thủy sẽ là một đối tượng bao lấy kiểu nguyên thủy mà nó hỗ trợ. Hay nói cách khác, mỗi lớp Wrapper sẽ chứa một kiểu nguyên thủy bên trong nó. Và, lớp Wrapper giúp xây dựng ra những phương thức khác nhằm bổ sung thêm cho công năng đơn giản ban đầu của kiểu nguyên thủy.

Có một điều bạn có thể ghi nhớ là, giống như với kiến thức về Chuỗi mà bạn đã biết, các lớp Wrapper cũng là các lớp có giá trị không thể thay đổi được, tiếng Anh gọi là Immutable. Bạn có thể xem thêm một tí kiến thức về Immutablebài này. Thêm nữa, các lớp Wrapper là các lớp final, và vì vậy bạn không thể nào tạo được lớp con của chúng.

Tại Sao Phải Dùng Đến Lớp Wrapper?

Trước tiên, điều cơ bản nhất cho chuyện này là, các lớp Wrapper sẽ giúp chúng ta chuyển đổi qua lại giữa một kiểu dữ liệu nguyên thủy sang kiểu dữ liệu đối tượng và ngược lại.

Bạn có thể xem ví dụ cho việc sử dụng kiểu dữ liệu nguyên thủy và kiểu Wrapper của nó như sau.

int a = 20; // a là biến có kiểu dữ liệu int nguyên thủy
Integer i = Integer.valueOf(a); // i là biến có kiểu dữ liệu đối tượng Integer, được tạo ra từ biến nguyên thủy a

Bạn có thể thấy, biến a là kiểu int, còn biến i là kiểu Integer.

Ý tiếp theo cho câu hỏi tại sao này là, nếu với các kiểu dữ liệu nguyên thủy, bạn chỉ có một chọn lựa là tạo ra biến rồi sử dụng giá trị của nó (nếu bạn không gán giá trị thì nó vẫn được tạo một giá trị mặc định, bạn có thể xem lại ở bài các kiểu dữ liệu nguyên thủy). Còn với các kiểu đối tượng, giá trị mặc định của nó là null, giá trị null này có thể được tận dụng trong một số trường hợp, như mang ý nghĩa chưa có giá trị chẳng hạn. Ngoài ra, các kiểu đối tượng còn mang theo nó nhiều phương thức hữu dụng, làm phong phú hơn tính ứng dụng của kiểu dữ liệu.

Thêm nữa, một số cấu trúc khác bên trong ngôn ngữ Java, như các cấu trúc về các danh sách mà chúng ta sẽ làm quen sau như ArrayList hay Vector đều chứa đựng các tập hợp kiểu dữ liệu đối tượng thay vì kiểu nguyên thủy, nên việc biết và vận dụng các lớp Wrapper là một bắt buộc.

Ngoài ra thì kiểu dữ liệu đối tượng sẽ thích hợp hơn trong việc thực thi đa luồng (multithreading) và đồng bộ hóa (synchronization) mà chúng ta sẽ cùng thảo luận ở bài viết khác.

Chuyển Đổi Qua Lại Giữa Kiểu Nguyên Thủy Và Kiểu Wrapper

Chuyển Đổi Kiểu Nguyên Thủy Sang Kiểu Wrapper

Việc chuyển đổi một kiểu nguyên thủy sang kiểu Wrapper của nó người ta gọi là Boxing. Không phải mang ý nghĩa là môn đấm bốc đâu. Boxing ở đây mang ý nghĩa là đóng hộp, tức là đóng dữ liệu nguyên thủy vào trong cái hộp Wrapper của nó đấy. Như ví dụ mà bạn xem trên kia, khi một kiểu int a được chuyển thành kiểu Integer i.

Bạn có thể thực hiện việc boxing thông qua các phương thức khởi tạo của các lớp Wrapper.

// Các dạng Boxing
int a = 500;
Integer i = new Integer(a);
Integer j = new Integer(500);
Float f = new Float(4.5);
Double d = new Double(5);
Character ch = new Character('a');
Boolean b = new Boolean(true);

Hoặc có thể gán trực tiếp các giá trị nguyên thủy vào cho các lớp Wrapper, cách này người ta còn gọi là Autoboxing, có nghĩa là hệ thống sẽ chuyển đổi một cách tự động.

// Các dạng Autoboxing
int a = 500;
Integer i = a;
Integer j = 500;
Float f = 4.5f;
Double d = 5d;
Character ch = 'a';
Boolean b = true;

// Đây cũng là một dạng Autoboxing mà bạn sẽ được biết khi học đến bài về Collection
ArrayList<Integer> arrInt = new ArrayList<Integer>();
arrInt.add(25);

Chuyển Đổi Kiểu Wrapper Sang Kiểu Nguyên Thủy

Ngược lại với trên kia, khi bạn chuyển từ một kiểu Wrapper sang kiểu nguyên thủy của nó người ta gọi là Unboxing, có nghĩa là mở hộp, tức là mở cái hộp Wrapper để lấy dữ liệu nguyên thủy ra.

Bạn có thể thực hiện việc unboxing thông qua các phương thức xxxValue(). Với xxx là đại diện cho từng loại dữ liệu, như với ví dụ sau.

int a = 500;
Integer i = a; // Autoboxing
int i2 = i.intValue(); // Unboxing
		
Integer j = 500; // Autoboxing
int j2 = j.intValue(); // Unboxing
		
Float f = 4.5f; // Autoboxing
float f2 = f.floatValue(); // Unboxing
		
Double d = 5d; // Autoboxing
double d2 = d.doubleValue(); // Unboxing
		
Character ch = 'a'; // Autoboxing
char ch2 = ch.charValue(); // Unboxing
		
Boolean b = true; // Autoboxing
boolean b2 = b.booleanValue(); // Unboxing
		
ArrayList<Integer> arrInt = new ArrayList<Integer>();
arrInt.add(25); // Autoboxing
int arr0 = arrInt.get(0).intValue(); // Unboxing

Cũng tương tự như autoboxing, kỹ thuật unboxing cũng có thể được viết như thế này.

int a = 500;
Integer i = a;
int i2 = i; // Unboxing
		
Integer j = 500;
int j2 = j; // Unboxing
		
Float f = 4.5f;
float f2 = f; // Unboxing
		
Double d = 5d;
double d2 = d; // Unboxing
		
Character ch = 'a';
char ch2 = ch; // Unboxing
		
Boolean b = true;
boolean b2 = b; // Unboxing
		
ArrayList<Integer> arrInt = new ArrayList<Integer>();
arrInt.add(25);
int arr0 = arrInt.get(0); // Unboxing

Các Phương Thức Hữu Ích Của Lớp Wrapper

Như trên kia mình có nói là các lớp Wrapper giúp tạo thêm cho các kiểu dữ liệu nguyên thủy vốn chỉ có tác dụng chứa đựng dữ liệu, trở thành một đối tượng với nhiều phương thức hữu dụng hơn. Sau đây mình sẽ liệt kê các phương thức hữu dụng của chúng để bạn tham khảo nhé.

parseXxx()

Tham số truyền vào cho phương thức static này là một chuỗi, kết quả nhận được là một giá trị nguyên thủy tương ứng với chuỗi truyền vào.

int i = Integer.parseInt("10");
float f = Float.parseFloat("4.5");
boolean b = Boolean.parseBoolean("true");
		
System.out.println(i);
System.out.println(f);
System.out.println(b);

Chắc bạn cũng đoán được kết quả in ra console của dòng code trên đây rồi đúng không nào.

toString()

Khác với toString() của lớp Object, toString() lần này của các lớp Wrapper là phương thức static, nó có một giá trị truyền vào là kiểu dữ liệu nguyên thủy tương ứng với lớp Wrapper đó, và kết quả trả về là một chuỗi tương ứng với giá trị truyền vào. Ví dụ này thử nghiệm với lớp Integer, các lớp Wrapper khác cũng sẽ tương tự.

String sI = Integer.toString(10);
System.out.println(sI);

Kết quả in ra console là chuỗi “10”.

xxxValue()

Bạn đã làm quen với phương thức này ở trên kia. Cụ thể thì các phương thức dạng này giúp chuyển đổi một giá trị của lớp Wrapper nào đó về kiểu dữ liệu nguyên thủy (unboxing). Thỉnh thoảng phương thức này còn giúp chuyển đổi kiểu dữ liệu như khi bạn ép kiểu tường minh một giá trị vậy. Bạn có thể dễ hiểu hơn khi xem ví dụ sau.

Double d = 50.5;
int i = d.intValue();
byte b = d.byteValue();
		
System.out.println(d);
System.out.println(i);
System.out.println(b);

Kết quả in ra console lần lượt là 50.5, 5050.

compareTo()

Nếu bạn còn nhớ, ở bài học về chuỗi chúng ta cũng đã nói qua phương thức compareTo() dùng để so sánh các giá trị chuỗi với nhau. Vậy thì đối với các lớp Wrapper mà chúng ta làm quen hôm nay, thì công năng của nó vẫn được áp dụng trọn vẹn. Tức là phương thức này sẽ được dùng để so sánh hai giá trị của hai lớp Wrapper (có cùng kiểu dữ liệu) với nhau.

Cụ thể, với việc bạn gọi lopWrapper1.compareTo(lopWrapper2), thì kết quả sẽ như sau.

– Nếu kết quả của phương thức trả về một số âm, thì lopWrapper1 sẽ có giá trị nhỏ hơn lopWrapper2.
– Nếu kết quả của phương thức trả về số 0, thì lopWrapper1 sẽ có giá trị bằng với lopWrapper2.
– Nếu kết quả của phương thức trả về một số dương, thì lopWrapper1 sẽ có giá trị lớn hơn lopWrapper2.

Bạn có thể xem ví dụ sau để hiểu rõ hơn.

Integer i = 50;
Integer i1 = Integer.parseInt("50");
Integer i2 = Integer.valueOf(52);
Integer i3 = 30;
		
System.out.println("CompareTo i & i1: " + i.compareTo(i1));
System.out.println("CompareTo i & i2: " + i.compareTo(i2));
System.out.println("CompareTo i & i3: " + i.compareTo(i3));

Kết quả in ra console sẽ là.

CompareTo i & i1: 0
CompareTo i & i2: -1
CompareTo i & i3: 1

compare()

Phương thức này có công dụng và cách sử dụng giống giống với compareTo() trên kia. Khác ở chỗ đây là phương thức static của mỗi lớp Wrapper, nên bạn có thể gọi trực tiếp từ lớp. Đồng thời tham số truyền vào là hai giá trị của hai lớp Wrapper. Kết quả trả về của compare() cũng mang một trong ba giá trị (âm, 0, dương) như với compareTo() trên đây.

Integer i1 = Integer.parseInt("50");
Integer i2 = Integer.valueOf(52);
		
System.out.println("Compare i1 & i2: " + Integer.compare(i1, i2));
		
Float f1 = new Float("20.25f");
Float f2 = new Float("2.43f");
		
System.out.println("Compare f1 & f2: " + Float.compare(f1,f2));

Kết quả in ra console là.

Compare i1 & i2: -1
Compare f1 & f2: 1

equals()

Tương tự như equals() khi so sánh chuỗi. Phương thức này sẽ so sánh các giá trị của các lớp Wrapper và trả về một kiểu boollean, với true là bằng nhau và false là khác nhau. Như ví dụ sau.

Integer i1 = Integer.parseInt("50");
Integer i2 = Integer.valueOf(50);
		
System.out.println("Compare i1 & i2: " + i1.equals(i2));
		
Float f1 = new Float("20.25f");
Float f2 = new Float("2.43f");
		
System.out.println("Compare f1 & f2: " + f1.equals(f2));

Kết quả in ra console là.

Compare i1 & i2: true
Compare f1 & f2: false

Bài Tập Số 1

Bạn hãy thử thực thi dòng code sau, và thử tìm hiểu xem tại sao dữ liệu in ra console lại như vậy nhé.

int i = Integer.parseInt("10");
float f = Float.parseFloat("4.5a");
		
System.out.println(i);
System.out.println(f);

Bài Tập Số 2

Bạn hãy cho biết kết quả in ra console của dòng code sau.

int i = Integer.parseInt("10.5");
		
System.out.println(i);

Bài Tập Số 3

Ngoài các phương thức hữu ích của các lớp Wrapper mà mình có liệt kê trên kia, bạn hãy tự tìm hiểu thêm các phương thức khác nữa nhé, chúng hữu ích không kém những gì mình nêu ra đâu đấy. Chẳng hạn như.

– Các phương thức static của các lớp Integer, Long, Float, và Double: toHexString(), toOctalString(), toBinaryString(), max(), min(),…
– Các phương thức static của lớp Character: isLowerCase(), isUpperCase(), isDigit(), toLowerCase(), toUpperCase(),…

Như vậy qua bài hôm nay, bạn đã biết được các kiểu dữ liệu nguyên thủy trong Java đã được ngôn ngữ này “bao bọc” bởi các lớp Wrapper, để làm cho chúng trở nên đa dạng và tiện dụng hơn như thế nào rồi đúng không.

Cảm ơn bạn đã đọc các bài viết của Yellow Code Books. Bạn hãy ủng hộ blog bằng cách:

Đánh giá 5 sao bên dưới mỗi bài nếu thấy thích.
Comment bên dưới mỗi bài nếu có thắc mắc.
Để lại địa chỉ email của bạn ở thanh bên phải để nhận được thông báo sớm nhất khi có bài viết mới.
Chia sẻ các bài viết của Yellow Code Books đến nhiều người khác.

Bài Kế Tiếp

Chúng ta sẽ nói đến kiến thức có tên là Biệt lệ. Các bạn sẽ được trang bị các cách thức để trở thành các chuyên gia “bắt lỗi” trong Java, mà bất kỳ một lập trình viên Java nào cũng buộc phải biết.

Advertisements
Rating: 5.0/5. From 4 votes.
Please wait...

2 comments

Gửi phản hồi