Kotlin - Need to know - part 1

Seri bài viết tổng hợp những kiến thức cơ bản trong Kotlin cần nhớ.

1. In Kotlin, everything is an object

In Kotlin, everything is an object in the sense that we can call member functions and properties on any variable. Some of the types can have a special internal representation - for example, numbers, characters and booleans can be represented as primitive values at runtime - but to the user they look like ordinary classes.

Giống những ngôn ngữ khác, Kotlin có định nghĩa sẵn những type như Int, Double, Boolean, Char... Tuy nhiên, trong Kotlin, mọi thứ đều là đối tượng theo nghĩa mọi thứ đều hoạt động như một đối tượng, chúng ta có thể gọi các member functionsmember properties trên bất cứ một biến (variable) nào.

Kotlin có thể diễn tả một số kiểu cơ bản như numbers, charactersbooleans là các giá trị nguyên thủy (primitive value) ở thời điểm runtime để tăng performance, nhưng với end user tất cả chúng đều là đối tượng.

Trong hầu hết các trường hợp như khai báo, Kotlin Basic Types sẽ compiled sang Java Primitive Types để tăng performance, ngoại trừ một vài trường hợp đặc biệt:

Khai báo Basic Type ở dạng Nullable Object:

var a : Long? = null 
a = 100L
val b = 200L

// the compiled code is below
Long a = (Long)null;
a = 100L;
long b = 200L;

Vì lý do trong Java platform, numbers được lưu vật lý dưới dạng JVM primitive type, trừ trường hợp chúng ta cần một nullable number reference (vd Long?...) hoặc generics, trong các trường hợp còn lại numbersBoxed.

// Note === is Equality operators / Referential equality, it will only be true 
// if both the objects or variables pointing to the same object.
val a: Long = 10000L
println(a === a) // Prints 'true'
val boxedA: Long? = a
val anotherBoxedA: Long? = a
println(boxedA === anotherBoxedA) // !!!Prints 'false'!!!

// Hypothetical code, does not actually compile:
val a: Int? = 1 // A boxed Int (java.lang.Integer)
val b: Long? = a // implicit conversion yields a boxed Long (java.lang.Long)
print(b == a) // Surprise! This prints "false" as Long's equals() checks whether the other is Long as well

Khai báo kiểu Array Object:

val a = arrayOf(1, 2, 3)
val b = intArrayOf(1, 2, 3)

// The compiled code is below
Integer[] var10000 = new Integer[]{1, 2, 3};
int[] var2 = new int[]{1, 2, 3};

Trong Kotlin, Arrayinvariant(Một khái niệm trong Generics, sẽ nói trong bài viết khác) nghĩa là ko cho phép gán một Array<String> cho một Array<Any>.

Kiểu Primitive type arrays (ByteArray, ShortArray, IntArray...) cho phép biểu diễn một mảng nguyên thủy mà không cần boxing overhead, các lớp này không có quan hệ kế thừa đối với lớp Array, nhưng có chung tập các phương thức và thuộc tính.

=> Như vậy, Kotlin không có khái niệm về kiểu nguyên thủy Primitive Types mà chỉ có Basic Types

2. Null-Safety and Platform Types

Bất cứ một tham chiếu nào từ Java đều có thể là null, điều này làm cho các yêu cầu nghiêm ngặc về null-safety trong Kotlin trở nên không thực tế đối với các đối tượng đến từ Java (điều này thường gặp trong android khi Android SDK đa số đang là Java code).

Types của các khai báo java được xử lý đặc biệt trong Kotlin và được gọi là những Platform Types. Null-check được nới lỏng cho các type này, do đó đảm bảo an toàn cho chúng giống như trong Java.

val list = ArrayList<String>() // non-null (constructor result)
list.add("Item")
val size = list.size // non-null (primitive int)
val item = list[0] // platform type inferred (ordinary Java object)

// 
item.substring(1) // allowed, may throw an exception if item == null

trong ví dụ trên đối tượng item (là một Java ArrayList item) được xem như là Platform Type, Kotlin sẽ không báo nullability error lúc compile time, nhưng lời gọi item.substring(1) trên có thể lỗi lúc runtime

Trong android, một ví dụ thường thấy là lúc findViewById

val textView = findViewById<TextView>(R.id.textView) // Platform type: TextView!
textView?.text = "Maybe I'm null"
textView.text = "Maybe I'm not null"

Platform typesnon-denotable, nghĩa là chúng ta không thể viết chúng xuống trực tiếp bằng ngôn ngữ, trong ví dụ trên không thể khai báo kiểu TextView!. Khi Platform type được gán cho một biến Kotlin, chúng ta có thể dựa trên Type inference (kiểu suy luận) hoặc có thể chọn type mà chúng ta expect (cả nullablenon-null đều được cho phép).

val nullable: String? = item // allowed, always works
val notNull: String = item // allowed, may fail at runtime

Notation for Platform Types

Như nói ở trên, Platform Type không thể được mentioned explicity (đề cập rõ ràng) trong chương trình, nên không có một cú pháp nào cho chúng trong ngôn ngữ. Tuy nhiên, trình biên dịch và IDE đôi khi cần hiển thị chúng (ví dụ trong error message, parameter...), vì vậy, Kotlin có 1 vài kí hiệu ghi nhớ cho Platform Type:

  • T! nghĩa là T? hoặc T
  • (Mutable)Collection<T>! nghĩa là Java collection của T có thể mutable hoặc immutable, có thể nullable hoặc không.
  • Array<out T>! nghĩa là Java array of T (hoặc một subtype of T) có thể nullable hoặc không

3. Kotlin Expression and Statement

Đây là 2 khái niệm quan trọng thường bị hiểu nhầm.

Expression

An expression in a programming language is a combination of one or more explicit values, constants, variables, operators and functions that the programming language interprets and computes to produce another value. (Theo wikipedia)

Một expression trong ngôn ngữ lập trình là sự kết hợp của một hay nhiều explicit values, constants, variables, operators and functions mà ngôn ngữ diễn giải và tính toán để tạo ra một giá trị khác.

Vì thế, đơn giản 1+1 là một expression, sumOf(1,2,3) cũng vậy. Chú ý rằng một expression có thể chứa expression khác, ví dụ expression sumOf(1, 2*3) chứa expression 2*3.

An expression is every part of code that returns value.

Tức là một expression là tất cả những phần của code có trả về giá trị. Do đó, có thể dựa theo kinh nghiệm rằng những phần code bạn có thể gán giá trị của nó cho một biến hoặc truyền nó như là một argument của method thì đó là một expression.

Vì mọi function trong Kotlin đều trả về ít nhất là Unit (chú ý rằng Unit có thể obmitted trong function), vì vậy mỗi function invocation / function call là một expression.

fun sayHello() {
   // function block
}
println(sayHello()) // print 
// sayHello() is an Expression 
// println(sayHello()) is an Expression that contains another Expression 
// and it is also a Statement because it is a standalone Expression

Trong java, if luôn là statement, nhưng trong kotlin, if hay when hoặc try được phép return giá trị nên bạn có thể sử dụng chúng như là một expression hoặc một statement.

// Traditional usage 
var max = a 
if (a < b) max = b

// use as statement with else 
var max: Int
if (a > b) {
    max = a
} else {
    max = b
}
// use as expression
val max = if (a > b) a else b

// if branches can be blocks, and the last expression is the value of a block:
val max = if (a > b) {
    print("Choose a")
    a // the value of this block
} else { // in this case the expression is required to have an else branch
    print("Choose b")
    b // the value of this block
}

// When
val color = when {
    relax -> GREEN
    studyTime -> YELLOW
    else -> BLUE
}

// Try
val response = try {
    gson.fromJson(json)
} catch (e: Throwable) {
    null
}

Trong Kotlin, return, break, continue là các structural jump expressions, type của những expression này là Nothing type (sẽ trình bày trong bài viết khác). Trong java, chúng đều là branching statement.

Statement

In computer programming, a statement is a syntactic unit of an imperative programming language that expresses some action to be carried out. (Theo wikipedia)

Trong lập trình, một statement là một đơn vị cú pháp (syntactic unit) của một ngôn ngữ lập trình mệnh lệnh, nó thể hiện một số hành động sẽ được thực hiện.

Trong Kotlin, một statement có thể là:

  • Variable declaration, ex: val a = 10
  • Variable or property assignment, ex a = 20
  • Local class declaration, ex: class A {}

Expression độc lập (Standalone expression) cũng là một statement Vd: updateUser(user).

Đến đây bạn có thể tự giải đáp một vài điểm khác giữa JavaKotlin

// Java
int a, b;
b = 2; // Java statement in this case (it is called assignment statement)
a = b = 2; // work only in Java because b = 2 in this case it is called assignment expression
// However, in Kotlin, b = 2 is already a statement.
a = b == 2 ? 2 : 1; // Java allowed ternary operator which not in Kotlin because Kotlin allow if return value 
a = if (b == 2) 2 else 1 // same as above Java statement
// and more...

TO BE CONTINUED

References: