Handsome codes with Kotlin
Kotlin là một ngôn ngữ rất linh hoạt, giúp cho đoạn code bạn viết ra dễ đọc và thoáng hơn rất nhiều nhờ vào một số các tính năng mới mà ngôn ngữ Java không có. Vậy các đặc điểm nào giúp cho việc code trở nên dễ hiểu và linh hoạt, chúng ta hãy cùng tìm hiểu nhé: Extension Functions Extension ...
Kotlin là một ngôn ngữ rất linh hoạt, giúp cho đoạn code bạn viết ra dễ đọc và thoáng hơn rất nhiều nhờ vào một số các tính năng mới mà ngôn ngữ Java không có. Vậy các đặc điểm nào giúp cho việc code trở nên dễ hiểu và linh hoạt, chúng ta hãy cùng tìm hiểu nhé:
Extension Functions
Extension Functions cho phép chúng ta có thể thêm các chức năng cho một class, bạn có thể viết thêm một hàm cho class mà không cần thiết phải kế thừa nó.
Để viết code sao cho dễ hiểu thì cách đặt tên hàm, tên biến chính là điều quan trọng nhất. Thử tưởng tượng bạn có một class ở thư viện bên thứ ba và bạn không thể sửa đổi hay cập nhật được, mà tác giả của thư viện đó đang đặt một tên hàm khó hiểu và bạn muốn thay đổi tên đó theo cách của mình. Thật dễ dàng, hãy dùng Extension Functions với một tên mới dễ hiểu hơn.
Ví dụ, tôi không muốn viết editText.getText().toString() để lấy giá trị String của Edittext, nó khá dài, hãy đơn giản hoá bằng cách thêm extension functions vào class EditText
fun EditText.asString(): String { return text.toString() }
Giờ đây, để lấy text của EditText ta sẽ gọi:
editText.asString()
Trông dễ đọc hơn đúng không nào. Hihi
Higher-order Functions
Higher-order Functions nói một cách dễ hiểu là ta có thể truyền một hàm như là một tham số vào một hàm khác. Hãy xem đoạn code dưới đây
inline fun SharedPreferences.edit(func: SharedPreferences.Editor.() -> Unit) { val editor = edit() editor.func() editor.apply() }
Giờ ta có thể lưu data với SharedPreferences như sau:
val prefs = defaultSharedPreferences prefs.edit { putString("foo", "bar") putInt("id", 1) }
func: SharedPreferences.Editor.() nghĩa là ta sẽ truyền một hàm func nào đó ở trong lớp SharedPreferences.Editor. Hoặc hiểu theo cách khác là bạn có thể truyền các hàm của lớp SharedPreferences.Editor như là một tham số. Higher-order functions cho phép chúng ta giảm được nhiều đoạn code thừa và giúp mọi việc linh hoạt hơn.
Thử cải thiện thêm chút nữa:
fun SharedPreferences.Editor.put(pair: Pair<String, Any>) { val key = pair.first val value = pair.second when(value) { is String -> putString(key, value) is Int -> putInt(key, value) is Boolean -> putBoolean(key, value) is Long -> putLong(key, value) is Float -> putFloat(key, value) else -> error("Only primitive types can be stored in SharedPreferences") } }
giờ đây, ta có thể sử dụng như sau:
val prefs = defaultSharedPreferences prefs.edit { put("foo" to "bar") put("id" to 1) }
Trông tuyệt hơn đúng không ạ?
Optional callback classes
Bạn có thể kết hợp việc sử dụng extension functions và higher-order functions để viết các câu lệnh một cách đơn giản và ngắn gọn hơn như trường hợp sau đây mà tác giả bài viết này có đặt tên là Optional callback classes
Bình thường ta có thể sử dụng lambda cho những hàm có interface với một callback method như là button.setOnClickListener(view -> doSomething()) hoặc cũng có thể viết button.setOnClickListener { doSomething() }. Tuy nhiên, trong trường hợp có nhiều hơn 1 callback method trong interface thì chúng ta vẫn sẽ phải implement interface và truyền tham số như là một anonymous inner classes giống với Java. Ví dụ:
animation.setAnimationListener(object : Animation.AnimationListener { override fun onAnimationRepeat(animation: Animation?) { // actually, I don't need this method but I have to implement this. } override fun onAnimationEnd(animation: Animation?) { doSomething() } override fun onAnimationStart(animation: Animation?) { // actually, I don't need this method but I have to implement this. } })
Ta có 2 phương thức không sử dụng tới nhưng vẫn phải thêm vào, điều này khiến cho code trông khá thừa. Vì thế ta hãy thử viết lại sử dụng higher-order functions và extension functions
Đầu tiên, viết một lớp implements Animation.AnimationListener.
class __AnimationListener : Animation.AnimationListener { private var _onAnimationRepeat: ((animation: Animation?) -> Unit)? = null private var _onAnimationEnd: ((animation: Animation?) -> Unit)? = null private var _onAnimationStart: ((animation: Animation?) -> Unit)? = null override fun onAnimationRepeat(animation: Animation?) { _onAnimationRepeat?.invoke(animation) } fun onAnimationRepeat(func: (animation: Animation?) -> Unit) { _onAnimationRepeat = func } override fun onAnimationEnd(animation: Animation?) { _onAnimationEnd?.invoke(animation) } fun onAnimationEnd(func: (animation: Animation?) -> Unit) { _onAnimationEnd = func } override fun onAnimationStart(animation: Animation?) { _onAnimationStart?.invoke(animation) } fun onAnimationStart(func: (animation: Animation?) -> Unit) { _onAnimationStart = func } }
Ta đặt 3 giá trị tương ứng với 3 hàm. Nó sẽ được gọi khi một callback tương ứng với nó được gọi.
Sau đó, ta cần một extension function mà giống với Animation#setAnimationListener(listener: AnimationListener)
inline fun Animation.setAnimationListener( func: __AnimationListener.() -> Unit) { val listener = __AnimationListener() listener.func() setAnimationListener(listener) }
Hàm này lấy các hàm của lớp __AnimationListener làm tham số.
Cuối cùng, ta có thể tạo listener như sau:
animation.setAnimationListener { onAnimationRepeat { // do something } onAnimationEnd { // do something } onAnimationStart { // do something } }
Ngoài ra, nếu bạn chỉ cần sử dụng một trong số những hàm này, bạn có thể bỏ đi bất cứ hàm nào không cần dùng đến.
animation.setAnimationListener { onAnimationEnd { // do something } }
Khi này thì nếu AnimationListener#onAnimationRepeat(animation: Animation) được gọi thì _onAnimationRepeat sẽ không gọi lên bất cứ hàm nào bởi vì ta không set cho nó.
Trông ngắn gọn hơn rất nhiều phải không nào?