Category Archives: 08.Lập trình hướng đối tượng

Bài 28-Extensions Method trong Kotlin-OOP phần 7


Kotlin hỗ trợ Extensions Method rất tuyệt với, giống như LINQ trong C#(bạn nào quan tâm thì đăng ký học khóa học LinQ C# ở đây). Extensions Method cho phép ta chèn thêm phương thức vào các Lớp có sẵn mà không cần sửa mã nguồn, không cần biết mã nguồn, giống như là cài đặt virus vào một chương trình nào đó.Cú pháp:

fun [Kiểu_Dữ_Liệu].Tên_Hàm([Các đối số]):[Kiểu_Trả_Về]

{

this ở trong này là đối tượng thuộc [Kiểu_Dữ_Liệu]

}

Khi khai báo như trên, thì bất kỳ [Kiểu_Dữ_Liệu] nào cũng có một hàm mới là Tên_Hàm

Ví dụ 1: Hãy cài đặt hàm Cong (Cộng) vào kiểu Int


/**
* Created by cafe on 02/06/2017.
*/
fun Int.Cong(a:Int):Int
{
return this+a
}
fun main(args: Array) {
var t=5.Cong(9)
println("t=$t")
var x1=9
var x2=10
var x3=x1.Cong(x2)
println("x3=$x3")
}

Bạn nhìn vào dòng 4 Tui khai báo Int.Cong ==> là cài đặt hàm Cong vào kiểu Int

dòng lệnh số 6 có từ khoa this, this ở đây chính là đối tượng hiện tại của kiểu Int.

Bạn tiếp tục quan sát dòng số 9, thấy số 5.Cong(9) . Vì 5 là số Nguyên kiểu Int mà kiểu Int ta vừa cài đặt hàm Cong nên hiển nhiên 5 sẽ có phương thức Cong. Bạn để ý là Ta không hề biết Kotlin tjao ra Int như thế nào nhưng ta vẫn có thể thêm phương thức Cong vào Int mà không cần biết mã nguồn cũ cũng không cần sửa mã nguồn cũ.

Khi chạy hàm main ở trên thì ta có kết quả sau:

t=14
x3=19

Ví dụ 2: Cài đặt hàm kiểm tra 1 số có phải là số Nguyên Tố hay không vào kiểu Int có sẵn của Kotlin


/**
* Created by cafe on 02/06/2017.
*/
fun Int.Cong(a:Int):Int
{
return this+a
}
fun Int.KiemTraNguyenTo():Boolean
{
var dem=0
for(i in 1..this)
{
if(this%i==0)
dem++
}
return dem==2
}
fun main(args: Array) {
var t=5.Cong(9)
println("t=$t")
var x1=9
var x2=10
var x3=x1.Cong(x2)
println("x3=$x3")

var a=7
if(a.KiemTraNguyenTo()==true)
{
println("$a là số nguyên tố")
}
else
{
println("$a Ko là số nguyên tố")
}
var b=9
if(b.KiemTraNguyenTo()==true)
{
println("$b là số nguyên tố")
}
else
{
println("$b Ko là số nguyên tố")
}
}

Dòng số 8 ở trên là cài đặt hàm KiemTraNguyenTo() vào kiểu Int

Dòng 27 và 36 là sử dụng hàm KiemTraNguyenTo() có sẵn trong Int (do ta cài vào).

Khi chạy hàm main kết quả như sau:

t=14
x3=19
7 là số nguyên tố
9 Ko là số nguyên tố

Khi chạy kết quả

Ví dụ 3:Cài đặt hàm Tính Tuổi cho Lớp Sinh Viên được viết sẵn của ai đó, Lớp Sinh Viên này có cấu trúc như sau:


import java.util.*

/**
* Created by cafe on 02/06/2017.
*/
class SinhVien {
private var ma:Int=0
private var ten:String?=null
private var namSinh:Date?=null
public var Ma:Int
get() {return ma}
set(value) {ma=value}
public var Ten:String?
get()= ten
set(value) {ten=value}
public var NamSinh:Date?
get() = namSinh
set(value) {namSinh=value}
constructor(ma: Int, ten: String?, namSinh: Date?) {
this.ma = ma
this.ten = ten
this.namSinh = namSinh
}
}

Vậy làm sao để cài đặt hàm tính tuổi cho lớp Sinh Viên này? (không được đổi mã nguồn cũ). Rất dễ dàng ta làm như sau:


import java.util.*

/**
* Created by cafe on 02/06/2017.
*/
fun SinhVien.Tuoi():Int
{
var cal=Calendar.getInstance()
var yearHienTai=cal.get(Calendar.YEAR)
cal.time=this.NamSinh
var yearNamSinh=cal.get(Calendar.YEAR)
return yearHienTai-yearNamSinh+1
}
fun main(args: Array) {
var ns=Calendar.getInstance()
ns.set(Calendar.YEAR,1998)
ns.set(Calendar.MONTH,2)
ns.set(Calendar.DAY_OF_MONTH,15)
var teo=SinhVien(1,"Nguyễn Văn Tèo",ns.time)
var tuoiCuaTeo=teo.Tuoi()
println("Tuổi của Tèo="+tuoiCuaTeo)
}

Dòng số 6 ở trên ta cài đặt hàm Tuoi() vào Lớp Sinh Viên. Dựa vào 3 ví dụ ở trên mà bạn có thể áp dụng vào các dự án bất kỳ.

Khi chạy hàm main ở trên thì kết quả là gì?

Tuổi của Tèo=20

Như vậy tới đây Tui đã trình bày xong Extensions Method trong Kotlin. Các bạn chú ý học kỹ, thực hành lại và có gắng hiểu được nó thông qua các ví dụ ở trên nhé. Các bài sau Tui sẽ trình bày về Xử lý File trong Kotlin rất quan trọng trong quá trình xử lý lưu trữ dữ liệu

Các bạn có thể tải source code bài này ở đây: http://www.mediafire.com/file/3wd89egcx10qim7/HocExtension.rar

Hẹn gặp các bạn ở những bài tiếp theo

Chúc các bạn thành công!

Trần Duy Thanh (http://communityuni.com/)

Bài 27-Alias và cơ chế gom rác tự động trong Kotlin-OOP phần 6


Như vậy hầu hết các bạn đã học xong hướng đối tượng cài đặt bằng Kotlin, trong bài này Tui sẽ nói thêm về Alias và cơ chế gom rác tự động trong Kotlin.

Alias là khả năng mà tại 1 ô nhớ có nhiều đối tượng cùng trỏ tới (>=2 đối tượng).

Cơ chế gom rác tự động còn gọi là garbage collection, nó tự động thu hồi bộ nhớ khi ô nhớ đó không còn đối tượng nào quan lý.

Hai khái niệm này vô cùng quan trọng, các bạn cần phải đảm bảo hiểu rõ nguộn ngành về nó để có thể kiếm soát vấn đề quản lý bộ nhớ khi thực thi các dự án.

Bây giờ Tui sẽ trình bày chi tiết quá trình tạo Alias và garbage collection trong Kotlin (các ngôn ngữ khác tương tự)

Vì Kotlin chạy trong nền JVM nên cơ chế hoạt động sẽ giống với Java.

Để cho có cảm giác về Alias và cơ chế gom rác tự động, ta tạo một lớp phân số như sau:


/**
* Created by cafe on 02/06/2017.
*/
class PhanSo {
var tu:Int=1
var mau:Int=1

constructor(tu: Int, mau: Int) {
this.tu = tu
this.mau = mau
}
}

Giả sử chúng ta có 2 đối tượng psA, psB như dưới đây:

psA=PhanSo(1,5)

psB=PhanSo(3,7)

Lúc này trên thanh RAM sẽ có 2 ô nhớ cấp phát cho 2 đối tượng phân số được quản lý bởi 2 biến đối tượng psA và psB (Chú ý là phải tưởng tượng nha các thím, chứ mình làm sao biết ô nhớ nào trên thanh RAM được cấp phát):

Hình trên cho thấy 2 đối tượng psA và psB quản lý 2 ô nhớ độc lập. Tức là psA thao tác trên vùng nhớ A sẽ không ảnh hưởng gì tới psB và ngược lại.

Bây giờ Giả sử ta thực hiện lệnh:

psA=psB

Thì lệnh trên: Ngôn ngữ nói “Phân số A bằng Phân số B”, nhưng hệ thống máy tính sẽ làm việc theo cơ chế “Phân số A trỏ tới vùng nhớ mà phân số B đang quản lý”. Hay nói cách khác “Vùng nhớ B” bây giờ có 2 biến đối tượng cùng trỏ tới(cùng quản lý):Như vậy đã xuất hiện Alias ở “vùng nhớ B”. Lúc này sẽ xảy ra 2 hiện tượng như sau:

  • Tại “vùng nhớ B”, nếu psA thay đổi thông tin sẽ làm cho psB thay đổi thông tin (vì cả 2 đối tượng này cùng quản lý một vùng nhớ)
  • “Vùng nhớ A” không còn đối tượng nào tham chiếu tới, lúc này hệ thống sẽ tự động thu hồi bộ nhớ (hủy vùng nhớ A đã cấp trước đó), cơ chế này gọi là cơ chế gom rác tự động

Hình sau minh họa GC (garbage collection):

Tức là trong hàm main ta có như sau:


/**
* Created by cafe on 02/06/2017.
*/
fun main(args: Array) {
var psA=PhanSo(1,5)
var psB=PhanSo(3,7)
psA=psB
println("Tử số của A="+psA.tu)
}

Nếu bạn đã hiểu những gì Tui giải thích ở trên thì: Dòng lệnh số 7 phát sinh ra 2 hiện tượng. Đầu tiên là Alias (psA và psB cùng trỏ tới 1 ô nhớ), Thứ hai là Cơ chế gom rác tự động sẽ tự động thu hồi bộ nhớ cấp cho psA ở dòng 5 (vì lúc này psA đã trỏ qua vùng nhớ của psB).

Khi chạy hàm main ở trên thì kết quả là gì?

Tử số của A=3

Vấn đề là bạn có hiểu vì sao Tử số của A bằng 3 hay không? rõ ràng bên trên Khai báo psA=PhanSo(1,5) thì Tử số của psA là 1 chứ sao là 3 được? hi hi  hi rõ ràng bạn phải hiểu dòng lệnh số 7 đã làm thay đổi điều đó rồi, lúc này psA đã trỏ qua vùng nhớ mà psB đang trỏ. Mà vùng nhớ psB đang trỏ thì tử số bằng mấy? nó bằng 3 chứ gì nữa ==> tử số của psA phải là 3 chứ không phải 1 vì psA đã trỏ qua vùng nhớ của psB rồi…. do you understand?

Tiếp tục nếu giả sử Tui sửa tiếp coding như sau:


/**
* Created by cafe on 02/06/2017.
*/
fun main(args: Array) {
var psA=PhanSo(1,5)
var psB=PhanSo(3,7)
psA=psB
println("Tử số của A="+psA.tu)
psA.tu=113
println("Tử số của B="+psB.tu)
}

Khi chạy hàm main ở trên thì kết quả là gì?

Tử số của A=3
Tử số của B=113

Bạn có hiểu vì sao Tử số của B=113 không? thật phi lý đúng không? rõ ràng dòng lệnh 9 mình chỉ đổi psA.tu=113, đâu có đổi tử số của phân số B đâu, tại sao tử số của B bị đổi theo A? Nó phải là 113 mới đúng, rất có lý không hề phi lý chút nào. Vì như trên Tui giải thích là vùng nhớ B có psA và psB cùng trỏ tới, do đó psA đổi cùng làm psB đổi và ngược lại, do đó khi psA.tu=113 tức là psB.tu cũng bằng 113. Các bạn cứ tưởng tượng thế này: Nếu Tui và các bạn cùng sử dụng chung 1 thẻ ATM, một ngày đẹp trời Tui buồn buồn Tui lấy hết sạch tiền trong ATM đó đi Nhậu –> bạn làm gì còn Cắc nào trong ATM đúng không?

Đôi khi trong quá trình thực hiện phần mềm ta có nhu cầu sao chép đối tượng ra (tạo thêm một đối tượng giống y xì đối tượng cũ nhưng nằm ở ô nhớ khác, để ta có thể tự do thay đổi thông tin trên đối tượng sao chép mà không làm ảnh hưởng tới đối tượng gốc). Kotlin hỗ trợ chúng ta hàm clone trong interface Cloneable để sao chép đối tượng:

Ta chỉnh là lớp Phân số như sau:


/**
* Created by cafe on 02/06/2017.
*/
class PhanSo:Cloneable {
var tu:Int=1
var mau:Int=1

constructor(tu: Int, mau: Int) {
this.tu = tu
this.mau = mau
}
fun copy():PhanSo
{
var ps:PhanSo=clone() as PhanSo
return ps
}
}

Ta thấy lớp Phân số kế thừa từ Cloneable  và Tui có tự bổ sung hàm copy() cho lớp phân số. hàm này đơn thuần là gọi lệnh clone() của Cloneable  để tạo ra 1 phiên bản mới của đối tượng (dữ liệu giống nhau y xì nhưng nằm trên ô nhớ khác nhau).

Trong main ta sửa lại như sau:


/**
* Created by cafe on 02/06/2017.
*/
fun main(args: Array) {
var psA=PhanSo(1,5)
var psB=PhanSo(3,7)
psA=psB
println("Tử số của A="+psA.tu)
psA.tu=113
println("Tử số của B="+psB.tu)
var psC=psA.copy()
psC.tu=114
println("Tử số của A="+psA.tu)
println("Tử số của C="+psC.tu)
println("Mẫu số của C="+psC.mau)
}

Bạn thấy dòng lệnh 11 Tui gọi hàm copy và lưu vào biên psC. Chú ý ngay chỗ này nó tạo ra thêm 1 ô nhớ mới và để cho psC quản lý. Tức là psC không liên can gì tới psA và psB (lúc này psA và psB vẫn đang cùng quản lý ô nhớ B).

Như vậy khi chạy đoạn lệnh trên kết quả là gì?

Tử số của A=3
Tử số của B=113
Tử số của A=113
Tử số của C=114
Mẫu số của C=7

Bạn hiểu vì sao ra kết quả ở trên không? Tui chủ ý không giải thích nữa, để những bạn nào không hiểu thì Comment vào bài học này, những bạn hiểu sẽ giải thích cho các bạn. Nhưng Tui rất mong muốn bạn phải tự hiểu.

Như vậy tới đây Tui đã trình bày xong alias và Cơ chế gom rác tự động trong Kotlin. Các bạn chú ý học kỹ, thực hành lại và có gắng hiểu được nó thông qua các ví dụ ở trên nhé. Các bài sau Tui sẽ trình bày về Extension method trong Kotlin rất là hay và rất là mới mẻ

Các bạn có thể tải source code bài này ở đây: http://www.mediafire.com/file/xx1k62gbwk2n7pk/HocGarbageCollection.rar

Hẹn gặp các bạn ở những bài tiếp theo

Chúc các bạn thành công!

Trần Duy Thanh (http://communityuni.com/)

Bài 26-Lập trình hướng đối tượng trong Kotlin – phần 5


Trong phần này Tui sẽ trình bày các kiến thức liên quan tới Kế Thừa trong lập trình Hướng đối tượng. Để học tốt bài này thì bắt buộc bạn phải đả thông kinh mạch các bài:

Bài 22-Lập trình hướng đối tượng trong Kotlin – phần 1

Bài 23-Lập trình hướng đối tượng trong Kotlin – phần 2

Bài 24-Lập trình hướng đối tượng trong Kotlin – phần 3

Bài 25-Lập trình hướng đối tượng trong Kotlin – phần 4

Trước khi đi vào kỹ thuật cài đặt Kế Thừa trong Kotlin, Tui điểm sơ sơ 1 xíu về Khái Niệm Kế Thừa.

Kế thừa là gì?

Kế thừa bạn có thể hiểu nôm na là Sự TÁI SỬ DỤNG lại mã nguồn cũ, có thể mở rộng thêm mã lệnh mới từ những thứ đã tồn tại, giúp nâng cao hiệu suất lập trình.

Thường các đối tượng có cùng chung một số đặc điểm, hành vi được nhóm lại với nhau.

Ví dụ:

  • Xe đạp
  • Xe máy
  • Xe hơi
  • Xe tải

các xe này có một số đặc điểm giống nhau đúng ko?

Ta thường thấy mô hình Lớp kế thừa người ta vẽ giống như dưới đây:

Hay Một lớp con có thể là lớp cha của các lớp khác:

Để làm tốt được Kế Thừa thì ta cần biết 2 khái niệm:

Tổng quát hoá: Những đặc điểm chung mà các lớp đều có==>là các lớp Cha

Chuyên biệt hóa: Những đặc điểm riêng chỉ có các lớp con mới có ==>là các lớp Con

Ví dụ: Công ty có 2 loại nhân viên (Nhân Viên Chính Thức và Nhân Viên Thời Vụ):

Thì rõ ràng Nhân viên không kể là Chính thức hay thời vụ thì mọi người đều có mã, tên. Nhưng Nhân viên chính thức và nhân viên thời vụ thì khác nhau ít nhất là cách tính lương. Do đó Lớp Tổng quát Hoát là lớp Nhân Viên vì nó có các đặc điểm chung của Chính Thức và Thời Vụ, Còn các lớp chuyên biệt hóa là: Nhân VIên CHính Thức, Nhân VIên Thời Vụ:Cài đặt Kế thừa trong Kotlin như thế nào?

Ta sẽ viết ví dụ bài Nhân Viên theo hình ở trên. Trong Kotlin, để viết Lớp cho phép kế thừa ta làm như sau:

Lớp Tổng quát hóa (lớp cha) Nhân Viên:


/**
* Created by cafe on 02/06/2017.
*/
open abstract class NhanVien {
protected var ma:Int=0
protected var ten:String=""
public abstract fun tinhLuong(ngayCong:Int):Double
}

Lớp cha NhanVien ở trên là một lớp trừu tượng, khi có phương thức trừu tượng tinhLuong. Vấn đề là làm sao biết được nên khai báo nó là phương thức trừu tượng? Ta hiểu nôm na như sau: Mọi nhân viên điều tính lương, nhưng ngay thời điểm này không biết tính lương cho nhân viên nào, mà không biết tính cụ thể cho ai thì khai báo nó là trừu tượng (tuy không biết là tinhLuong cho đối tượng nào nhưng chắc chắn nó phả có tinhLuong). Với abstract hay interface thì các hàm trừu tượng nó giống như là các luật, các mẫu đưa ra và yêu cầu các lớp con kế thừa phải tuân thủ theo (override lại toàn bộ).

Một điều cần lưu ý nữa là Nếu muốn các lớp khác có thể kế thừa từ lớp này thì bắt buộc ta phải thêm từ khóa open đằng trước khai báo lớp như mẫu code ở trên.

Bây giờ ta tạo 1 lớp NhanVienChinhThuc kế thừa từ lớp NhanVien như sau:

Bạn thấy ta dùng dấu 2 chấm (:) để kế thừa nhé, nhìn vào hình trên là ta biết cách viết NhanVienChinhThuc kế thừa từ NhanVien phải viết như thế nào.

Ở trên bạn thấy nó báo lỗi màu đỏ, thực ra là do lớp NhanVien là lớp trừu tựu. Thì các lớp con kế thừa từ lớp trừu tượng bắt buộc phải Override lại toàn bộ phương thức trừu tượng của lớp cha. IntelliJ IDEA hỗ trợ chúng ta công cụ overrid này rất nhanh chóng. Đó là ngày chỗ báo lỗi bạn nhấn tổ hợp phím Alt+Enter nó sẽ xuất hiện ra màn hình có dòng Implement members như hình trên, ta chọn nó rồi nhấn Enter, màn hình Implement Members sẽ xuất hiện như dưới đây:

Ta chọn các phương thức trừu tượng của lớp cha rồi bấm OK, nó sẽ tự động phát sinh Coding như sau:


/**
* Created by cafe on 02/06/2017.
*/
class NhanVienChinhThuc:NhanVien() {
override fun tinhLuong(ngayCong: Int): Double {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}

Giả sử rằng nhân viên chính thức có lương: Nếu NgayCong>=22 thì lương là 10 triệu, còn <22 ngày thì lương 10 triệu – mỗi ngày nghỉ mất 500 nghìn, ta sửa lại coding như sau:


/**
* Created by cafe on 02/06/2017.
*/
class NhanVienChinhThuc:NhanVien() {
override fun tinhLuong(ngayCong: Int): Double {
if(ngayCong>=22)
return 10000000.0
return 10000000.0-500000*(22-ngayCong)
}
}

Tương tự như vậy ta Tạo lớp NhanVienThoiVu kế thừa từ NhanVien. Và lương  của Nhân viên thời vụ là 150k*số ngày công:


/**
* Created by cafe on 02/06/2017.
*/
class NhanVienThoiVu:NhanVien() {
override fun tinhLuong(ngayCong: Int): Double {
return ngayCong*150000.0
}

}

Cách sử dụng các lớp Kế thừa trong Kotlin như thế nào?

Tạo một tập tin Kotlin để thử nghiệm


/**
* Created by cafe on 02/06/2017.
*/
fun main(args: Array) {
var an=NhanVienChinhThuc()
var binh=NhanVienThoiVu()
var luongAn=an.tinhLuong(20)
println("Lương của An="+luongAn)
var luongBinh=binh.tinhLuong(3)
println("Lương của Bình="+luongBinh)
}

Kết quả khi chạy ta thấy:

Lương của An=9000000.0
Lương của Bình=450000.0

Ở trên các bạn thấy Tui hay nhắc tới Override, vậy nó là cái gì? (bạn đã từng học Overloading đúng ko), bạn có thể hiểu nôm na Overriding Method như sau:

  • Trong một tập các lớp có mối quan hệ huyết thống có các phương thức giống signature y xì (nội dung phương thức khác nhau)
  • Overriding methods giúp lập trình viên có thể định nghĩa cách hành xử khác nhau ứng với các đối tượng khác nhau nhưng cùng sử dụng một tên phương thức.
  • Ví dụ: Nhân viên chính thức và Nhân viên thời vụ đều có phương thức là Tính Lương, tuy nhiên cách thức tính lương của 2 đối tượng này sẽ khác nhau.

Bạn tự so sánh sự khác nhau giữa Overloading Method và Overriding Method.

Còn cách kế thừa nào khác trong Kotlin không?

Trong Kotlin còn có thể dùng Interface để Kế thừa (implements), Interface không phải là GUI mà là tập các giao ước (các luật, các Rule) khi các lớp con implents(hay gọi là kế thừa ) thì phải định nghĩa lại toàn bộ các phương thức này.

Giả sử ta có interface:


interface MyInterface {
fun bar()
fun foo() {
// optional body
}
}

Thì khi lớp con kế thừa ta có thể viết như sau:


class Child : MyInterface {
override fun bar() {
// body
}
}

Chúng ta chú ý là với Kotlin kế thừa từ Lớp cũng chỉ cho đơn thừa kế giống như Java , C#. Kế thừa từ Interface cho phép đa thừa kế Interface (đa dẫn xuất), vì vậy đôi khi nó bị conflict nếu như các Interface mà nó kế thừa có các phương thức trừu tượng trùng tên nhau. Trường hợp này Kotlin giải quyết như sau:


interface A {
fun foo() { print("A") }
fun bar()
}
interface B {
fun foo() { print("B") }
fun bar() { print("bar") }
}
class C : A {
override fun bar() { print("bar") }
}
class D : A, B {
override fun foo() {
super<A>.foo()
super<B>.foo()
}
override fun bar() {
super<B>.bar()
}
}

Như vậy tới đây Tui đã trình bày xong phần kế thừa trong Kotlin, còn rất nhiều phần khác liên quan tới Kế thừa trong Kotlin, các bạn tự xử nhé (ví dụ như tính đa hình, seal classes). Các bạn chú ý học kỹ, thực hành lại và có gắng hiểu được nó thông qua các ví dụ ở trên nhé. Các bài sau Tui sẽ trình bày về alias và cơ chế gom rác tự động trong Kotlin, các bạn chú ý theo dõi

Các bạn có thể tải source code bài này ở đây: http://www.mediafire.com/file/448pk2x91l1n016/HocOOPPhan5.rar

Hẹn gặp các bạn ở những bài tiếp theo

Chúc các bạn thành công!

Trần Duy Thanh (http://communityuni.com/)

Bài 25-Lập trình hướng đối tượng trong Kotlin – phần 4


Trong bài 24 Tui đã trình bày chi tiết các loại phương thức, tham chiếu this, overloading, parameter list… là một trong những kỹ thuật rất quan trọng trong lập trình hướng đối tượng. Trong bài này Tui tiếp tục trình bày về Data Classes, Nested Classes, Inner Classes, Enum Classes trong Kotlin là một trong những kỹ thuật khá thú vị trong lập trình hướng đối tượng và Kotlin.

1)Data Classes

Trong quá trình xử lý, ta rất thường xuyên chỉ cần lưu trữ dữ liệu mà không có các phương thức. Kotlin hỗ trợ chức năng này bằng cách giúp ta tạo một lớp đặc biệt, lớp này gọi là Data Class.

Các Data Class trong Kotlin sẽ tự động cung cấp:

  • equals() / hashCode()
  • toString()
  • componentN()
  • copy()

Ví dụ:


/**
* Created by cafe on 01/06/2017.
*/
fun main(args: Array) {
data class User(var UserName:String,var Password:String)
var user1=User(UserName = "obama",Password = "113@114Xa")
var user1Copy=user1.copy()
var user2Copy=user1.copy(Password ="cohangxom" )
println(user1.toString())
println(user1Copy.toString())
println(user2Copy.toString())
println("User Name của user 1="+user1.UserName)
}

Ở dòng số 5, ta thấy một Data Class tên là User được tạo ra, nó có 2 thuộc tính là UserNamePassword

Kết quả khi chạy ta thấy:

User(UserName=obama, Password=113@114Xa)
User(UserName=obama, Password=113@114Xa)
User(UserName=obama, Password=cohangxom)
User Name của user 1=obama

2)Nested Class

Kotlin giống như các ngôn ngữ lập trình khác, đó là khả năng cho phép Lớp này nằm trong lớp khác dạng Nested. Cần lưu ý là các lớp Nested sẽ không thể truy suất được các biến thành viên trong Outer class

ví dụ:


/**
* Created by cafe on 01/06/2017.
*/
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = 113
}
}

Ở ví dụ trên thì phương thức foo() trong Nested class không thể truy suất được tới biến bar trong Outer class

Khi triệu khoi foo() ta làm như sau:


/**
* Created by cafe on 01/06/2017.
*/
fun main(args: Array) {
val demo = Outer.Nested().foo()
println(demo)
}

Chạy chương trình ta sẽ được kết quả là 113 xuất ra màn hình.

3) Inner Classes

Kotlin giống như các ngôn ngữ lập trình khác, đó là khả năng cho phép Lớp này nằm trong lớp khác dạng Inner. Cần lưu ý là các lớp Inner sẽ có thể truy suất được các biến thành viên trong Outer class


package inner

/**
* Created by cafe on 01/06/2017.
*/
class Outer {
private val bar: Int = 1
inner class Inner {
fun foo() = bar
}
}

Code ở trên ta thấy dùng từ khóa inner đằng trước class Inner

hàm main sử dụng như sau:


package inner

import inner.Outer

/**
* Created by cafe on 01/06/2017.
*/
fun main(args: Array) {
val demo = Outer().Inner().foo()
println(demo)
}

Chạy chương trình ta sẽ được kết quả là 1 xuất ra màn hình.

Ở đây ta để ý có sự khác biệt giữa Nested Class và Inner class cả cách khai báo cũng như cách truy suất (hãy tự để ý kỹ nó khác chỗ nào).

4)Enum Classes

Enum cũng là một loại Lớp đặc biệt trong Kotlin (giống như các ngôn ngữ khác C#, java…)

Cách khai báo Enum trong Kotlin rất đơn giản, tương tự như các ngôn ngữ lập trình khác.

Lúc new File ta chọn Kind là Enum class, Name là nơi đặt tên Enum. Ví dụ đặt là XepLoai:


enum class XepLoai {
XuatSac,Gioi,Kha,TrungBinh,Yeu,Kem
}

Ta có thể sử dụng Enum này như sau:


/**
* Created by cafe on 01/06/2017.
*/
class SinhVien {
var Ma:Int=0
var Ten:String=""
var DiemTrungBinh:Double=0.0
public fun XepLoaiHocTap():XepLoai
{
if(DiemTrungBinh>=9)return XepLoai.XuatSac
if(DiemTrungBinh>=8)return XepLoai.Gioi
if(DiemTrungBinh>=7)return XepLoai.Kha
if(DiemTrungBinh>=5)return XepLoai.TrungBinh
if(DiemTrungBinh>=3)return XepLoai.Yeu
return XepLoai.Kem
}
}

Tạo main để triệu gọi như sau:


fun main(args: Array) {
var teo=SinhVien()
teo.DiemTrungBinh=9.0
println(teo.XepLoaiHocTap())
}

Chạy chương trình ta sẽ có kết quả là XuatSac.

Như vậy tới đây Tui đã trình bày xong các loại class đặc biệt trong Kotlin(Data Classes, Nested Classes, Inner Classes và Enum classes). Các bạn chú ý học kỹ, thực hành lại và có gắng hiểu được nó thông qua các ví dụ ở trên nhé. Các bài sau Tui sẽ tiếp tục trình bày về OOP trong Kotlin (Kế thừa rất quan trọng), các bạn chú ý theo dõi

Các bạn có thể tải source code bài này ở đây: http://www.mediafire.com/file/8r7z95j8r9lptob/HocOOPPhan4.rar

Hẹn gặp các bạn ở những bài tiếp theo

Chúc các bạn thành công!

Trần Duy Thanh (http://communityuni.com/)

Bài 24-Lập trình hướng đối tượng trong Kotlin – phần 3


Trong bài 23 Tui đã trình bày các quy tắc tạo lớp, constructor, thuộc tính, getter/setter, phương thức.  Trong bài này Tui tiếp tục trình bày về các Loại phương phức, tham chiếu this và Kỹ thuật Overloading Method trong Kotlin.

1)Các loại phương thức trong OOP:

Trong lập trình hướng đối tượng, Người ta chia phương thức ra làm 2 loại Service method và Support method.

Service Method là các phương thức public để cung cấp ra ngoài cho các đối tượng sử dụng

Support Method là các phương thức private dùng để hỗ trợ cho các Service method. Các đối tượng ở ngoài không thể truy suất tới các Support Method

Ví dụ:


/**
* Created by cafe on 01/06/2017.
*/
class TamGiac {
private var canhA: Double = 0.0
private var canhB: Double = 0.0
private var canhC: Double = 0.0

public var CanhA:Double
get() {return canhA}
set(value) {canhA=value}
public var CanhB:Double
get() {return canhB}
set(value) {canhB=value}
public var CanhC:Double
get() {return canhC}
set(value) {canhC=value}

/**
* Đây là service method
*/
public fun chuVi(): Double {
return canhA + canhB + canhC.toDouble()
}

/**
* Đây là support method
*/
private fun nuaChuVi(): Double {
return chuVi() / 2
}
/**
* Đây là service method
*/
public fun dienTich(): Double {
val p = nuaChuVi()
return Math.sqrt(p * (p - canhA) * (p - canhB) * (p - canhC))
}
}

Ở Trên bạn quan sát phương thức nuaChuVi() là support method vì nó để private, phương thức này sẽ hỗ trợ các Service method khác như: dienTich()

Ở Ngoài chỉ có thể truy suất được các phương thức là Service Method, không thể truy suất được tới Support Method, ví dụ:


/**
* Created by cafe on 01/06/2017.
*/
fun main(args: Array) {
var tg1= TamGiac()
tg1.CanhA=12.0
tg1.CanhB=17.0
tg1.CanhC=14.0
var dt=tg1.dienTich()
var cv=tg1.chuVi()

//var nucv=tg1.nuaChuVi() //lệnh này sẽ báo lỗi vì nuaChuVi() ko thể truy suất
println("Diện tích =$dt")
println("Chu vi = $cv")
}

Kết quả khi chạy ta thấy:

Diện tích =83.0267276243018
Chu vi = 43.0

2)Tham chiếu this:

Tham chiếu this rất quan trọng trong các ngôn ngữ lập trình, C#, Java…cũng sử dụng tham chiếu this. Để hiểu được tham chiếu this ta cần đi từ khái niệm Instance variablelocal variable

Instance variable là các thuộc tính, các biến khai báo ngoài lớp. Toàn bộ các hàm trong lớp điều có thể truy suất được.

Local variable là các biến được khai báo trong đối số của hàm hoặc nội dung hàm. Chỉ có hàm này mới truy suất được các biến local variable của nó, các đối số trong hàm thường được mặc định là readOnly(val)

Hình dưới đây sẽ minh họa 2 loại biến trên:Rõ ràng ta thấy biến banKinh ở dòng lệnh số 5 là instance variable. Biến banKinh ở dòng số 6 và biến bk ở dòng 8 là local variables.

Khi chúng ta rành các quy tắc về OOP, ta có thể dùng công cụ để tạo ra các constructor, các hàm. Rất thường xuyên chúng ta gặp trường hợp instance variable và local variable trùng tên nhau. Vậy làm sao để phân biệt được là chương trình đang gọi loại biến nào. Ví dụ ta quan sát dòng lệnh số 11 ở trên:

this.banKinh=banKinh

Chúng ta có lưu ý quan trọng sau:

Nếu tại một dòng lệnh mà đồng thời cùng truy suất tới instance variable và local variable(cùng tên) thì chương trình sẽ ưu tiên truy suất tới biến local variable

Tức là nếu như dòng lệnh số 11 ta xóa từ khóa this đi, sẽ trở thành:Vì như đã nói ở trên, Nếu tại một vị trí mà cùng truy suất tới instance và local cùng tên thì chương trình sẽ ưu tiên xử lý cho local. Tức nó nó sẽ cùng tham chiếu tới biến local. Như hình trên Tui vẽ 2 biến banKinh ở dòng 11 cùng trỏ tới biến banKinh ở dòng 6=> Như vậy rõ ràng nó sinh ra lỗi (lỗi thứ nhất là tham chiếu sai ý định, lỗi thứ 2 là mặc định các biến khai báo trong đối số của hàm thì Kotlin cho là readOnly(val) nên không thể đổi giá trị được).

Vì vậy trong trường hợp này bắt buộc ta phải dùng từ khóa this nhằm chỉ thị rõ cho chương trình : đây là đối tượng hiện tại trong lớp, phải truy suất tới Instance Variable. Do đó ta phải dùng từ khóa this là vậy, lúc này tham chiếu sẽ như sau:

3)Kỹ thuật Overloading Method

Overloading Method là một trong những kỹ thuật rất quan trọng trong lập trình OOP, Kotlin cũng hỗ trợ tính năng này.

  • Overloading Là đặc điểm trong cùng 1 lớp có nhiều phương thức cùng tên nhưng khác nhau về Signature.
  • Signature bao gồm: Số lượng các đối số hoặc kiểu dữ liệu các đối số hoặc thứ tự các đối số.
  • Kiểu dữ liệu trả về không được tính vào signature
  • Lợi ích của Overloading là khả năng tái sử dụng lại phương thức và giúp việc gọi hàm “uyển chuyển”.
  • Các Constructor là trường hợp đặc biệt của Overloading Method

Ví dụ:

ở ví dụ trên bạn thấy constructor Tui tạo có 3 constructor với số lượng các đối số khác nhau. Và có 2 hàm printInfor với số lượng đối số khác nhau. Đây chính là 2 ví dụ là Overloading Method ==>Giống Tên nhưng khác Signature

Khi chúng ta truy suất tới các phương thức này, thì dựa vào thông số đầu vào mà chương trình tự động điều hướng gọi chính xác phương thức.


/**
* Created by cafe on 01/06/2017.
*/
fun main(args: Array) {
//Constructor 0 đối số được gọi
var coca=SanPham()
//Constructor 2 đối số được gọi
var pepsi=SanPham(1,"Pepsi")
//Constructor 3 đối số được gọi
var biaSaiGon=SanPham(2,"Bia Sài Gòn",15.5)
//Phương thức 0 đối số được gọi
pepsi.printInfor()
//Constructor 3 đối số được gọi
coca.printInfor(5,"Cô Ca Cô La la",25.0)
}

Chắc chắn trong quá trình Coding chúng ta sẽ sử dụng overloading rất là nhiều vì lợi ích to lớn của nó, bài học này giúp các bạn biết được cách cài đặt kỹ thuật overloading, khi vào dự án cụ thể bạn có thể áp dụng.

Ngoài ra Kotlin còn hỗ trợ một loại Overloading Method đặc biệt đó là vararg (còn gọi là Parameter list) . Đây chính là trường hợp đặc biệt của Overloading Method (signature là số lượng các đối số khác nhau). Khi khai báo vararg  ta có thể truyền bao nhiêu đối số vào hàm cũng được.

Ví dụ:


/**
* Created by cafe on 01/06/2017.
*/
fun printNumbers(vararg numbers: Int) {
for (number in numbers) {
println(number)
}
}

fun main(args: Array) {
printNumbers(1)
printNumbers(7,8)
printNumbers(9,10,11,12)
}

ở trên hàm printNumbers là 1 Overloading Method đặc biệt, ta có thể truyền bao nhiêu đối số cũng được.

Kết quả khi chạy ta thấy:

1
7
8
9
10
11
12

Như vậy tới đây Tui đã trình bày xong các loại phương thức trong Kotlin, tham chiếu this, kỹ thuật Overloading Method. Đây là một trong kiến thức rất quan trọng trong lập trình OOP và Kotlin. Các bạn chú ý học kỹ, thực hành lại và có gắng hiểu được nó thông qua các ví dụ ở trên nhé. Các bài sau Tui sẽ tiếp tục trình bày về OOP trong Kotlin ( Data Classes, Nested Class, Enum Class trong Kotlin), các bạn chú ý theo dõi

Các bạn có thể tải source code bài này ở đây: http://www.mediafire.com/file/9t1b3sji51dscl5/HocOOPPhan3.rar

Hẹn gặp các bạn ở những bài tiếp theo

Chúc các bạn thành công!

Trần Duy Thanh (http://communityuni.com/)

Bài 23-Lập trình hướng đối tượng trong Kotlin – phần 2


Trong bài 22 Tui đã điểm qua một số đặc điểm của OOP cùng với một ví dụ về tạo Lớp và sử dụng Lớp trong Kotlin. Trong bài này Tui sẽ đi vào giải thích chi tiết từng thành phần trong quá trình tạo Lớp.

Cấu trúc của một class thường có:

  • Tên Lớp
  • Các constructors
  • Các thuộc tính
  • Các getter/setter
  • Các phương thức

1) Khai báo Tên Lớp trong Kotlin:

Với Kotlin, để tạo một lớp ta dùng từ khóa class giống như các ngôn ngữ lập trình khác:

class SinhVien {
}

Không giống với các ngôn ngữ khác, Kotlin cho phép khai báo chỉ mỗi Tên Lớp mà không cần có bất kỳ thành phần nào khác, cách khai báo này như sau:

class SinhVien

Tên lớp ta nên đặt theo quy tắc: Ký Tự Đầu Tiên của các Từ phải Viết Hoa, không chứa khoảng trắng…

Kotlin hỗ trợ 2 loại Constructors (những hàm đặc biệt, mặc định để khởi tạo các giá trị ban đầu cho các đối tượng khi cấp phát bộ nhớ) đó là primary constructor  secondary constructors

2)Khai báo primary constructor trong Kotlin:

class SinhVien constructor(ma:Int,ten:String){
}

Ở trên constructor là từ khóa, bên trong nó có 2 đối số, có nghĩa là Lớp SinhVien này có 1 Primary construtor có 2 đối số.

Nếu như primary construtor không có chứa các chú thích (annotations) và các modifiers(private, protected,public) thì có thể loại bỏ từ khóa constructor khỏi khai báo, tức là ta có thể Khai báo ngắn gọi như sau:

class SinhVien (ma:Int,ten:String){
}

Một lưu ý quan trọng là primary constructor không thể chứa bất kỳ đoạn mã nào, nếu muốn khởi tạo các giá trị mặc định cho các biến khi dùng primary constructor thì ta phải dùng khối lệnh init{} , ví dụ:

class SinhVien (ma:Int,ten:String){
init {
println("Đây là primary constructor")
println("Mã=$ma ; Tên =$ten")
}
}

Khi khai báo biến để sử dụng lớp SinhVien ở trên ta sẽ làm như sau:

fun main(args: Array) {
 var lanh=SinhVien(113,"Trần Thị Long Lanh")
}

Chạy chương trình, ta được kết quả như sau:

Đây là primary constructor
Mã=113 ; Tên =Trần Thị Long Lanh

Khi khai báo var lanh=SinhVien(113,”Trần Thị Long Lanh”) nó sẽ tự động tìm tới primary constructor và tự nhảy vào init block để thực hiện.

Và chú ý là không cần dùng từ khóa new để xin cấp phát bộ nhớ như C# hay java…

3)Khai báo secondary constructor trong Kotlin:

Ta cũng dùng từ khóa constructor để khai báo secondary constructor trong kotlin, ví dụ:


class SinhVien {
constructor()
{
println("Đây là secondary constructor 0 đối số")
}
constructor(ma:Int,ten:String)
{
println("Đây là secondary constructor 2 đối số")
println("Mã=$ma ; Tên =$ten")
}
}

Khai báo sử dụng các secondary constructor:


fun main(args: Array) {
var teo=SinhVien()
var lanh=SinhVien(113,"Trần Thị Long Lanh")
}

Khi chạy phần mềm ta có các thông báo sau:

Đây là secondary constructor 0 đối số
Đây là secondary constructor 2 đối số
Mã=113 ; Tên =Trần Thị Long Lanh

4) Khai báo các thuộc tính của Lớp trong Kotlin:

Thuộc tính là những gì thuộc về đối tượng, ví dụ như 1 Sinh Viên có các thông tin: mã, tên, năm sinh… thì các thông tin này là các thuộc tính của đối tượng Sinh Viên

Kotlin giống như các ngôn ngữ khác, cung cấp một số các Visibily Modifiers (private, protected, public, default) cho các thuộc tính:

 Modifiers Lớp (Class) Gói (Package) Lớp con (Subclass) Ngoài
public
protected Không
Không có (no modifier – default) Không
private Không Không Không

Ví dụ:


class SinhVien {
var ma:Int=0
var ten:String=""
constructor()
{
println("Đây là secondary constructor 0 đối số")
}
constructor(ma:Int,ten:String)
{
println("Đây là secondary constructor 2 đối số")
println("Mã=$ma ; Tên =$ten")
}
}

Theo khai báo ở trên thì 2 thuộc tính ma và ten để là default Modifier(không chỉ định rõ loại nào), thì trong cùng một package các đối tượng có thể truy suất tới các thuộc tính này:


fun main(args: Array) {
var teo=SinhVien()
teo.ma=114
teo.ten="Nguyễn Thị Tèo"
println("Thông tin của Tèo:")
println("Mã ="+teo.ma)
println("Tên="+teo.ten)
}

Để đám bảo tính đóng gói thì các thuộc tính nên khai báo private, khi thuộc tính khai báo là private. Lúc này các đối tượng không thể truy suất trực tiếp vào các thuộc tính được mà phải thông qua getter/setter:


class SinhVien {
private var ma:Int=0
private var ten:String=""
}

Khi khai báo Modifier là private thì ta không thể lấy tên đối tượng  truy suất các thuộc tính được, tức là khai báo như dưới đây sẽ bị báo lỗi:


fun main(args: Array) {
var teo=SinhVien()
teo.ma=114
teo.ten="Nguyễn Thị Tèo"
}

5) Khai báo các getter/setter của Lớp trong Kotlin:

Nếu các thuộc tính trong lớp mà để default và truy suất trong cùng package hoặc các thuộc tính để modifier là public thì ta không cần khai báo getter/setter.

Cách khai báo getter/setter trong Kotlin có khác biệt so với Java, C# và các ngôn ngữ khác.

Cú pháp tổng quát để khai báo getter/setter:

var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]

Ví dụ:


class SinhVien {
private var ma:Int=0
private var ten:String=""
public var Ma:Int
get()
{
return ma
}
set(value)
{
ma=value
}
public var Ten:String
get()
{
return ten
}
set(value)
{
ten=value
}
constructor()
{
println("Đây là secondary constructor 0 đối số")
}
constructor(ma:Int,ten:String)
{
println("Đây là secondary constructor 2 đối số")
println("Mã=$ma ; Tên =$ten")
}
}

Theo cách viết ở trên ta có 2 Getter/Setter Ma và Ten. Các đối tượng ở ngoài sẽ không truy suất được vào 2 thuộc tính maten mà chỉ được truy suất thông qua 2 Getter/Setter MaTen (chú ý IN HOA- in thường)

Các từ khóa get, set ở trên thì phần mềm IntelliJ IDEA cũng gợi ý sẵn cho ta, chỉ cần gõ ký tự đầu là nó gợi ý rồi sau đó ta nhấn Enter tự nó xuất hiện. Với set bạn để ý có value là parameter mặc định, đó là parameter nhận giá trị input từ ngoài vào (là thay đổi thông tin cho thuộc tính của đối tượng).

Ví dụ triệu gọi lớp SinhVien:


fun main(args: Array) {
var hanh=SinhVien()
hanh.Ma=113
hanh.Ten="Hồ Thị Hạnh"
println("Thông tin của Hạnh:")
println("Mã:"+hanh.Ma)
println("Tên:"+hanh.Ten)
}

Kết quả khi chạy các đoạn lệnh ở trên:

Đây là secondary constructor 0 đối số
Thông tin của Hạnh:
Mã:113
Tên:Hồ Thị Hạnh

Có rất nhiều trường hợp để hiệu chỉnh cách viết get và set ở trên, khi nào gặp trường hợp cụ thể thì các bạn tìm hiểu thêm, Tui không có trình bày ra hết ở đây được.

6) Khai báo các Phương thức của Lớp trong Kotlin:

Phương thức hay còn gọi là hành vi, các xử lý nghiệp vụ trên đối tượng. Đây là tập hợp các sức mạnh của đối tượng. Kotlin dùng từ khóa fun để khai báo các phương thức. Các phương thức này có thể có giá trị trả về hoặc không.

Ví dụ dưới đây bổ sung 2 phương thức (trả về giá trị String và không trả về giá trị nào cả):


/**
* Created by cafe on 01/06/2017.
*/
class SinhVien {
private var ma:Int=0
private var ten:String=""
public var Ma:Int
get()
{
return ma
}
set(value)
{
ma=value
}
public var Ten:String
get()
{
return ten
}
set(value)
{
ten=value
}
constructor()
{
println("Đây là secondary constructor 0 đối số")
}
constructor(ma:Int,ten:String)
{
println("Đây là secondary constructor 2 đối số")
println("Mã=$ma ; Tên =$ten")
}
fun xuatThongTin()
{
println("Thông tin chi tiết:")
println("Mã = "+ma)
println("Tên= "+ten)
}
fun chiTiet():String
{
var s="Thông Tin Chi Tiết:"
s=s.plus("\nMã="+ma)
s=s.plus("\n")
s=s.plus("Tên="+ten)
return s
}
}

xuatThongTin() là phương thức không trả về giá trị (còn gọi là các thủ tục) nó thực hiện nội tại bên trong Lớp. chiTiet() là phương thức trả về giá trị, ở ngoài khi truy suất có thể lưu lại kết quả của hàm này để sử dụng cho các mục đích khác (rất thường xuyên sử dụng trường hợp này).

Ví dụ dưới đây minh hoạt cách thức sử dụng 2 hàm trên:


/**
* Created by cafe on 01/06/2017.
*/
fun main(args: Array) {
var hanh=SinhVien()
hanh.Ma=113
hanh.Ten="Hồ Thị Hạnh"
println("Thông tin của Hạnh:")
println("Mã:"+hanh.Ma)
println("Tên:"+hanh.Ten)

println("--------Gọi phương thức xuatThongTin()---------")
hanh.xuatThongTin()
println("--------Gọi phương thức chiTiet()---------")
var detail=hanh.chiTiet()
println(detail)
}

Kết quả khi chạy ta thấy:

Đây là secondary constructor 0 đối số
Thông tin của Hạnh:
Mã:113
Tên:Hồ Thị Hạnh
——–Gọi phương thức xuatThongTin()———
Thông tin chi tiết:
Mã = 113
Tên= Hồ Thị Hạnh
——–Gọi phương thức chiTiet()———
Thông Tin Chi Tiết:
Mã=113
Tên=Hồ Thị Hạnh

Kotlin cũng giống như C# và Java, nó hỗ trợ một phương thức đặc biệt đó là toString() dùng để tự động xuất thông tin khi đối tượng được xuất ra màn hình/giao diện. Để tạo phương thức này rất đơn giản, trong lớp ta chỉ cần gõ từ khóa to thì phần mềm tự hiển thị hàm tostring cho ta:

Nó sẽ tự động ra hàm sau:

override fun toString(): String {
    return super.toString()
}

Ta muốn xuất thông tin gì thì cứ return đúng thông tin đó trong này, ví dụ:


override fun toString(): String {
var s="Thông Tin Chi Tiết:"
s=s.plus("\nMã="+ma)
s=s.plus("\n")
s=s.plus("Tên="+ten)
return s
}

Trong main, chỉ cần xuất đối tượng là nó tự động triệu gọi hàm toString(), rất lợi hại. Đối tượng chứa rất nhiều thông tin và các mối quan hệ, còn toString() chỉ là giúp ta xuất một vài thông tin cần thiết, nó giúp ta ẩn dấu được nhiều thông tin khác mà vẫn đảm bảo được tính chất của đối tượng:


/**
* Created by cafe on 01/06/2017.
*/
fun main(args: Array) {
var hanh = SinhVien()
hanh.Ma = 113
hanh.Ten = "Hồ Thị Hạnh"
println("Xuất thông tin đối tượng->tự gọi toString()")

println(hanh)
}

Kết quả khi chạy ta thấy:

Đây là secondary constructor 0 đối số
Xuất thông tin đối tượng->tự gọi toString()
Thông Tin Chi Tiết:
Mã=113
Tên=Hồ Thị Hạnh

Như vậy tới đây Tui đã trình bày xong cách thức tạo cấu trúc của 1 Lớp trong Kotlin, bao gồm các quy tắc tạo:

  • Tên Lớp
  • Các constructors
  • Các thuộc tính
  • Các getter/setter
  • Các phương thức

Các bạn chú ý học kỹ, thực hành lại và có gắng hiểu được nó thông qua các ví dụ ở trên nhé. Các bài sau Tui sẽ tiếp tục trình bày về OOP trong Kotlin (các loại phương thức, overloading method, tham chiếu this), các bạn chú ý theo dõi

Các bạn có thể tải source code bài này ở đây: http://www.mediafire.com/file/eqs3538mpwoplm2/HocOOPPhan2.rar

Hẹn gặp các bạn ở những bài tiếp theo

Chúc các bạn thành công!

Trần Duy Thanh (http://communityuni.com/)

Bài 22-Lập trình hướng đối tượng trong Kotlin – phần 1


Kotlin giống như các ngôn ngữ lập trình khác đó là hỗ trợ cài đặt lập trình hướng đối tượng. Và các bạn cần chú ý rằng, Lập trình hướng đối tượng là một khái niệm chung, ngôn ngữ lập trình chỉ là một trong những công cụ để triển khai khái niệm đó mà thôi. Điều này có nghĩa là nếu bạn đã hiểu OOP(object oriented programming) rồi thì C++, C#, Java, PHP, Python, Kotlin, R, GO….hay bất kỳ một ngôn ngữ nào khác sẽ dùng chung khái niệm OOP này để cài đặt, chỉ là kỹ thuật xử lý OOP của các ngôn ngữ này nó khác nhau.

Bài này Tui sẽ điểm qua một số khái niệm liên quan tới OOP, sau đó ta sẽ làm quen với một ví dụ cài đặt OOP bằng Kotlin. Những khái niệm về OOP nó đầy rẫy trên mạng, các bạn có thể tự search và đọc thêm các chi tiết khác.

Một số đặc điểm của OOP:

  • Tập trung vào dữ liệu thay cho các hàm.
  • Chương trình được chia thành các đối tượng độc lập.
  • Cấu trúc dữ liệu được thiết kế sao cho đặc tả được các đối tượng.
  • Dữ liệu được che giấu, bao bọc.
  • Các đối tượng trao đổi với nhau thông qua các hàm.
  • Chương trình được thiết kế theo hướng tiếp cận từ dưới lên (bottom-up): Phương pháp xây dựng chương trình bottom-up là phương pháp thường dùng để xây dựng phần mềm lớn, phức tạp. Ý tưởng của phương pháp này là xuất phát từ nhiều thành phần nhỏ đã có sẵn, ta khéo kết hợp chúng lại để tạo ra thành phần chức năng lớn hơn, ta tiếp tục kết hợp các thành phần xây dựng được để tạo ra thành phần lớn hơn nữa… cho đến khi xây dựng được chương trình giải quyết được bài toán mong muốn.

Một số ưu điểm nổi bật của OOP:

  • Không có nguy cơ dữ liệu bị thay đổi tự do trong chương trình.
  • Khi thay đổi cấu trúc dữ liệu của một đối tượng, không cần thay đổi mã nguồn của các đối tượng khác.
  • Có thể sử dụng lại mã nguồn, tiết kiệm tài nguyên.
  • Phù hợp với các dự án phần mềm lớn, phức tạp.

Khái niệm đối tượng (object) trong lập trình hướng đối tượng giống như một đối tượng cụ thể trong thế giới thực.

Mỗi đối tượng có các thuộc tính và các hành vi riêng:

  • Thuộc tính (attribute) mô tả đặc điểm của đối tượng.
  • Hành vi là phương thức hoạt động của đối tượng, gọi tắt là phương thức (method).

Ví dụ 1: Đối tượng Phân số

Đặc điểm của Phân số (thuộc tính) có:

  • Tử số
  • Mẫu số

Các Thao tác trên phân số (hành vi)

  • Cộng, trừ, nhân, chia
  • Tối giản
  • Nghịch đảo

Ví dụ 2: Đối tượng Xe hơi

Đặc điểm của Xe hơi (thuộc tính) có:

  • Hiệu xe
  • Màu xe
  • Số bánh xe
  • Số cửa

Các Thao tác trên Xe hơi (hành vi):

  • Chạy tới
  • Chạy lui
  • Dừng xe

Các đối tượng có các đặc điểm (thuộc tính và phương thức) giống nhau được gom nhóm thành một lớp để phân biệt với các đối tượng khác và dễ quản lý.

Một lớp (class) là sự phân loại của các đối tượng hay là kiểu (type) của đối tượng.

Ví dụ 3:

−Các chiếc xe Toyota, Honda, Porsche thuộc lớp xe hơi.

– Các con chó giữ nhà, chó săn, chó kiểng thuộc lớp chó.

-Bia Ken, Tiger, Sài gòn đỏ.. được gom lại thành một lớp Bia chẳng hạn

Như vậy Lớp(Class) là một khái niệm trừu tượng, dùng để chỉ một tập hợp các đối tượng có mặt trong hệ thống.

Lớp có thuộc tính và phương thức:

  • Thuộc tính của lớp tương ứng với thuộc tính của đối tượng.
  • Phương thức của lớp tương ứng với các hành động của đối tượng.

Một Lớp có thể có một trong các khả năng sau:

  • Không có thuộc tính, không có phương thức
  • Hoặc chỉ có thuộc tính, không có phương thức.
  • Hoặc chỉ có phương thức, không có thuộc tính.
  • Hoặc có cả thuộc tính và phương thức, trường hợp này là phổ biến nhất.
  • Có các mối quan hệ với các lớp khác

Gói (package)

Một nhóm các lớp (classes) và giao diện (interfaces) được tổ chức thành một đơn vị quản lý theo hình thức không gian tên gọi là package.

Lợi ích của package là tổ chức sắp xếp lại hệ thống thông tin các lớp trong dự án một cách khoa học, giúp cho việc theo dõi bảo trì dự án được tốt nhất.

Tính trừu tượng:

Trường hợp 1:

Lớp (Class) là một khái niệm trừu tượng, đối tượng là một thể hiện cụ thể của lớp.

Ví dụ 4:

Bản thiết kế của chiếc xe hơi là lớp.

Chiếc xe hơi được tạo ra từ bản thiết kế là đối tượng.

Trường hợp 2:

Từ những đối tượng giống nhau: trừu tượng hóa thành một lớp: Chỉ đưa ra các thuộc tính và phương thức cần thiết của đối tượng trong lập trình.

Tui nghĩ bạn nào cũng học Sinh Học, bạn thấy trong Sách giáo khoa người ta cũng phân ra : Lớp Chim, Lớp động vật có vú, lớp động vật ăn cỏ…. đó chính là sự trừu tượng hóa, tự tập các đối tượng họ trừu tượng thành các lớp.

Tính đóng gói:

  • Mỗi lớp được xây dựng để thực hiện một nhóm chức năng đặc trưng của riêng lớp đó.
  • Tất cả mọi thao tác truy xuất vào thành phần dữ liệu từ đối tượng này qua đối tượng khác phải được thực hiện bởi các phương thức (method) của chính đối tượng chứa dữ liệu.
  • Tính đóng gói cho phép dấu thông tin của đối tượng bằng cách kết hợp thông tin và các phương thức liên quan đến thông tin trong đối tượng.

Tính kế thừa:

  • Cho phép xây dựng một lớp mới dựa trên các định nghĩa của một lớp đã có.
  • Lớp đã có gọi là lớp Cha, lớp mới phát sinh gọi là lớp Con
  • Lớp con kế thừa tất cả các thành phần của lớp Cha, có thể mở rộng các thành phần kế thừa và bổ sung thêm các thành phần mới.

ví dụ về sự phát triển các dòng sẽ theo thời gian:

Ví dụ về kế thừa:

Ví dụ về kế thừa trong Employee:

Dưới đây là ví dụ về tạo một Lớp trong Kotlin (trong bài này Tui sẽ không giải thích chi tiết các thành phần trong Kotlin, các bài sau Tui sẽ trình bày rõ):

Ta tạo một Project Kotlin với tên HocOOPPhan1 rồi thêm 1 Class Kotlin theo hình dưới đây:

Bấm chuột phải vào src/ chọn Kotlin File/Class:

Ở màn hình New Kotlin File/Class ta chọn mục Kind là Class, đặt tên lớp là TamGiac–>bấm OK đê tạo

Kết quả đầu tiên khi tạo 1 Lớp trong Kotlin:

Tiến hành code cho TamGiac (bạn cứ làm theo, Tui sẽ giải thích kỹ các thành phần ở bài sau):

chi tiết lớp TamGiac:


/**
* Created by cafe on 01/06/2017.
*/
class TamGiac {
var a:Double=0.0
var b:Double=0.0
var c:Double=0.0
constructor()
constructor(a:Double,b:Double,c:Double)
{
this.a=a
this.b=b
this.c=c
}
fun chuVi():Double
{
return a+b+c
}
fun dienTich():Double
{
var p=chuVi()/2
return Math.sqrt(p*(p-a)*(p-b)*(p-c))
}
}

Tiếp tục tạo 1 file Kotlin app_test_tamgiac.kt để sử dụng TamGiac:


/**
* Created by cafe on 01/06/2017.
*/
fun main(args: Array) {
var tg1=TamGiac(4.0,5.0,6.0)
println("Thông tin tam giác 1:")
println("Chu vi="+tg1.chuVi())
println("Diện Tích="+tg1.dienTich())

var tg2=TamGiac()
tg2.a=7.5
tg2.b=10.3
tg2.c=15.5
println("Thông tin tam giác 2:")
println("Chu vi="+tg2.chuVi())
println("Diện Tích="+tg2.dienTich())
}

Kết quả khi chạy ta thấy:

Thông tin tam giác 1:
Chu vi=15.0
Diện Tích=9.921567416492215
Thông tin tam giác 2:
Chu vi=33.3
Diện Tích=33.354424275499014

Như vậy tới đây Tui đã trình bày xong một số khái niệm về OPP cũng như cách tạo một Lớp và sử dụng nó như thế nào trong Kotlin, các bạn chú ý học kỹ và hiểu được nó thông qua các ví dụ ở trên nhé. Các bài sau Tui sẽ trình bay chi tiết từng thành phần của Class trong Kotlin

Các bạn có thể tải source code bài này ở đây: http://www.mediafire.com/file/1ms2t5jdfjxo94k/HocOOPPhan1.rar

Hẹn gặp các bạn ở những bài tiếp theo

Chúc các bạn thành công!

Trần Duy Thanh (http://communityuni.com/)

%d bloggers like this: