-3

Kotlin: Constructors

Giống như nhiều dev Android khác, mình cũng bắt đầu từ Java và sau đó chuyển sang Kotlin vì nó đã trở ngôn ngữ chính và được sử dụng rộng rãi hơn do Google và cộng đồng phát triển Android. Một trong những sự khác biệt đầu tiên mà mình gặp phải khi chuyển từ Java để Kotlin là việc định nghĩa constructors.

Trong Java bạn không có hoặc nhiều constructors định nghĩa một cách rõ ràng . Nếu bạn không định nghĩa một constructor default, thì mặc định nó sẽ là một constructors rỗng. Constructors được định nghĩa trong phần thân của các class Java và mỗi constructors được xác định được phân biệt từ những tham số khi mà ta overloading (ta có thể tạo 1 constructors có 1 params và 1 constructor có 2 params ).

Ngược lại trong Kotlin thì bạn sẽ có primary constructors được tùy chọn định nghĩa trong signature của class. Ngoài các constructors chính bạn có thể xác định bằng constructors rỗng hoặc nhiều secondary constructors.

Dưới đây là một ví dụ về một primary constructor đơn giản trong Kotlin:

class Animal constructor(val name: String) {}

Lưu ý:

  • Việc sử dụng các keyword constructor và làm thế nào nó xuất hiện trong signature của class, trước khi body class được xác định. Keyword này là không bắt buộc trong ví dụ này và nó chỉ được cần thiết khi chúng ta sử dụng annotations (vd: @Inject) hoặc hiển thị modifiers (vd: thực hiện cho các constructor private)
  • Các primary constructor không thể chứa code. Code khởi tạo thì thông thường sẽ xuất hiện trong constructor này thay vì phải đưa vào init { }.

Và từ đó thì ví dụ trên có thể được viết lại để bỏ qua các constructor keyword và kết quả sẽ là như nhau:

class Animal (val name: String)

Một ví dụ về một primary constructor và nơi constructor keyword thực sự cần thiết là một class mà bạn không muốn khởi tạo bên ngoài class. Ví dụ có lẽ bạn muốn tạo tất cả các trường hợp mới của class Animal của bạn được tạo ra từ một factory method:

class Animal private constructor(val name: String) {

    companion object {
        fun newAnimal(name: String) = Animal(name)
    }
}

Một điểm khác biệt giữa constructor trong Kotlin khi chạy so với Java như thế nào là cách thức chúng được gọi, hay nói cách khác như thế nào new instances được tạo ra. Trong Kotlin không có từ khóa mới. Thay vào đó các new instances của một class được tạo ra tương tự như thể này:

val animal01 = Animal(name = "Dog")

Trong Kotlin nếu bạn không xác định một cách rõ ràng một primary constructor và bạn sẽ tạo sau chẳn hạn. Ví dụ, đoạn code sau thì hoàn toàn chấp nhận được:

class EmptyAnimal

New instances của EmptyAnimal có thể được tạo ra bằng cách gọi tạo primary constructor no-arg như sau:

val emptyAnimal = EmptyAnimal()

Nếu bạn muốn có một số loại khởi tạo code để chạy khi instantiating class của bạn thì bạn sẽ làm điều này với một hoặc nhiều blocks khởi tạo:

class Animal constructor(val name: String) {

    init {
        registerAnimalWithAKC()
    }

    private fun registerAnimalWithAKC() {
        // TODO: perform registration tasks
    }
}

Lưu ý với initialization blocks ( init { }):

  • Initialization blocks có liên quan đến primary constructor
  • Cho dù bạn định nghĩa một cách rõ ràng một primary constructor hay không, mỗi initialization blocks được xác định sẽ chạy khi class của bạn được khởi tạo
  • Nếu có nhiều hơn một initialization blocks được xác định thì sau đó nó sẽ thực hiện theo thứ tự mà chúng xuất hiện trong body class. Chạy theo code từ trên xuống dưới ấy.
  • Fields xuất hiện trong primary constructor có thể được tham chiếu từ các initialization blocks (trong ví dụ trên bạn có thể thấy rằng trường tên được tham chiếu trong initialization blocks)
  • Trong trường hợp bạn đã xác định bất kỳ secondary constructors sau đó lưu ý rằng các initialization blocks được xác định sẽ thực hiện trước thời điểm thực hiện body của bất kỳ secondary constructor nào.

Về secondary constructor. Bây giờ mình sẽ nói về nó nhé : Do không có default arguments trong Java bạn sẽ thường thấy một anti-pattern được gọi là Telescoping Constructor nơi constructors đang overloaded một lần nữa và một lần nữa cho nó có thể 1 constructer mới số lượng với params mới. Vd về anti-pattern:

private String name;
private String breed;
private boolean registered;

public Animal() {
    this("Dog");
}
public Animal(String name) {
    this(name, "Fish");
}
public Animal(String name, String breed) {
    this(name, breed, false);
}
public Animal(String name, String breed, boolean registered) {
    this.name = name;
    this.breed = breed;
    this.registered = registered;
}

Anti-pattern này có thể tránh được bằng cách sử dụng các Builder pattern, nhưng trong Kotlin này chủ yếu là không cần thiết bởi vì với Kotlin bạn có thể khai báo các giá trị mặc định cho tham số constructor:

class Animal (val name: String, 
           val breed: String = "Dog", 
           val registered: Boolean = false)

Với những gì đã nói vẫn còn nhiều trường hợp bạn có thể thấy rằng bạn cần phải xác định second (or secondary) constructor or constructors. Ví dụ, hãy tưởng tượng một copy constructor cho phép sao chép các giá trị của một thể hiện Animal hiện có để cho một trường hợp Animal mới:

class Animal (val name: String) {
    constructor(animal: Animal) : this(animal.name)
}

val animal01 = Animal(name = "Dog")
val animal02 = Animal(animal01)

Lưu ý về secondary constructors:

  • Chúng phải được bắt đầu bằng keyword constructor
  • Chúng phải gọi các primary constructor trực tiếp hoặc gián tiếp thông qua một secondary constructor. Gọi các primary constructor là thực hiện với các từ khóa this như trong ví dụ trên.
  • Initializer blocks sẽ luôn luôn được thực hiện trước thời điểm chạy của phần thân secondary constructors
  • Các parameters tại secondary constructors quy định sẽ không trở thành thuộc tính hoặc các fields của class. Chúng không thể được bắt đầu bằng var hoặc val. Nói cách khác, bạn sẽ cần phải gán những parameters thông qua fields hoặc làm điều gì đó với họ trong phần thân của secondary constructor.

Bài viết của mình đến đây là hết. Cảm ơn các bạn đã dành thời gian đọc nó.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí